GUI -- Buttons added to topo and device views that navigate to new flows table view.
Change-Id: Ibea4415d3c1fc717e609aebcd2205d0bba01c96d
diff --git a/web/gui/src/main/webapp/app/fw/nav/nav.js b/web/gui/src/main/webapp/app/fw/nav/nav.js
index ad85d90..36ef599 100644
--- a/web/gui/src/main/webapp/app/fw/nav/nav.js
+++ b/web/gui/src/main/webapp/app/fw/nav/nav.js
@@ -21,7 +21,7 @@
'use strict';
// injected dependencies
- var $log;
+ var $log, $location, $window, fs;
// internal state
var navShown = false;
@@ -52,9 +52,29 @@
return false;
}
+ function navTo(path, params) {
+ var url;
+ if (!path) {
+ $log.warn('Not a valid navigation path');
+ return null;
+ }
+ $location.url('/' + path);
+
+ if (fs.isO(params)) {
+ $location.search(params);
+ } else if (params !== undefined) {
+ $log.warn('Query params not an object', params);
+ }
+
+ url = $location.absUrl();
+ $log.log('Navigating to ', url);
+ $window.location.href = url;
+ }
+
angular.module('onosNav', [])
- .controller('NavCtrl', [
- '$log', function (_$log_) {
+ .controller('NavCtrl', ['$log',
+
+ function (_$log_) {
var self = this;
$log = _$log_;
@@ -62,15 +82,22 @@
$log.log('NavCtrl has been created');
}
])
- .factory('NavService', ['$log', function (_$log_) {
- $log = _$log_;
+ .factory('NavService',
+ ['$log', '$location', '$window', 'FnService',
- return {
- showNav: showNav,
- hideNav: hideNav,
- toggleNav: toggleNav,
- hideIfShown: hideIfShown
- };
+ function (_$log_, _$location_, _$window_, _fs_) {
+ $log = _$log_;
+ $location = _$location_;
+ $window = _$window_;
+ fs = _fs_;
+
+ return {
+ showNav: showNav,
+ hideNav: hideNav,
+ toggleNav: toggleNav,
+ hideIfShown: hideIfShown,
+ navTo: navTo
+ };
}]);
}());
diff --git a/web/gui/src/main/webapp/app/fw/widget/tooltip.js b/web/gui/src/main/webapp/app/fw/widget/tooltip.js
index 163a01d..d08393c 100644
--- a/web/gui/src/main/webapp/app/fw/widget/tooltip.js
+++ b/web/gui/src/main/webapp/app/fw/widget/tooltip.js
@@ -22,11 +22,11 @@
'use strict';
// injected references
- var $log, $timeout, fs;
+ var $log, fs;
// constants
var hoverHeight = 35,
- hoverDelay = 500,
+ hoverDelay = 100,
exitDelay = 100;
// internal state
@@ -104,19 +104,23 @@
}
}
- angular.module('onosWidget')
- .factory('TooltipService', ['$log', '$timeout', 'FnService',
+ function resetTooltip() {
+ tooltip.style('display', 'none').text('');
+ }
- function (_$log_, _$timeout_, _fs_) {
+ angular.module('onosWidget')
+ .factory('TooltipService', ['$log', 'FnService',
+
+ function (_$log_, _fs_) {
$log = _$log_;
- $timeout = _$timeout_;
fs = _fs_;
init();
return {
showTooltip: showTooltip,
- cancelTooltip: cancelTooltip
+ cancelTooltip: cancelTooltip,
+ resetTooltip: resetTooltip
};
}]);
}());
diff --git a/web/gui/src/main/webapp/app/view/device/device.css b/web/gui/src/main/webapp/app/view/device/device.css
index 25a6245..a0894bb 100644
--- a/web/gui/src/main/webapp/app/view/device/device.css
+++ b/web/gui/src/main/webapp/app/view/device/device.css
@@ -66,6 +66,14 @@
margin: 8px 0;
}
+#device-details-panel .top div.left {
+ float: left;
+ padding: 0 18px 0 0;
+}
+#device-details-panel .top div.right {
+ display: inline-block;
+}
+
#device-details-panel td.label {
font-style: italic;
padding-right: 12px;
@@ -73,8 +81,12 @@
color: #777;
}
-#device-details-panel hr {
- margin: 12px 0;
+#device-details-panel .actionBtns div {
+ padding: 12px 0;
+}
+#device-details-panel .top hr {
+ width: 95%;
+ margin: 0 auto;
}
.light #device-details-panel hr {
diff --git a/web/gui/src/main/webapp/app/view/device/device.js b/web/gui/src/main/webapp/app/view/device/device.js
index fa4dcf9..236f374 100644
--- a/web/gui/src/main/webapp/app/view/device/device.js
+++ b/web/gui/src/main/webapp/app/view/device/device.js
@@ -22,7 +22,7 @@
'use strict';
// injected refs
- var $log, $scope, fs, mast, ps, wss, is;
+ var $log, $scope, fs, mast, ps, wss, is, bns, ns, ttip;
// internal state
var self,
@@ -36,6 +36,7 @@
ctnrPdg = 24,
scrollSize = 17,
portsTblPdg = 50,
+ flowPath = 'flow',
pName = 'device-details-panel',
detailsReq = 'deviceDetailsRequest',
@@ -67,7 +68,7 @@
}
function setUpPanel() {
- var container, closeBtn;
+ var container, closeBtn, tblDiv;
detailsPanel.empty();
container = detailsPanel.append('div').classed('container', true);
@@ -77,7 +78,12 @@
addCloseBtn(closeBtn);
iconDiv = top.append('div').classed('dev-icon', true);
top.append('h2');
- top.append('table');
+
+ tblDiv = top.append('div').classed('top-tables', true);
+ tblDiv.append('div').classed('left', true).append('table');
+ tblDiv.append('div').classed('right', true).append('table');
+
+ top.append('div').classed('actionBtns', true);
top.append('hr');
bottom = container.append('div').classed('bottom', true);
@@ -95,13 +101,29 @@
addCell('value', value);
}
- function populateTop(tbody, details) {
+ function populateTop(tblDiv, btnsDiv, details) {
+ var leftTbl = tblDiv.select('.left')
+ .select('table')
+ .append('tbody'),
+ rightTbl = tblDiv.select('.right')
+ .select('table')
+ .append('tbody');
+
is.loadEmbeddedIcon(iconDiv, details._iconid_type, 40);
top.select('h2').html(details.id);
propOrder.forEach(function (prop, i) {
- addProp(tbody, i, details[prop]);
+ // properties are split into two tables
+ addProp(i < 3 ? leftTbl : rightTbl, i, details[prop]);
});
+
+ bns.button(btnsDiv,
+ 'dev-dets-p-flows',
+ 'flowsTable',
+ function () {
+ ns.navTo(flowPath, { devId: details.id });
+ },
+ 'Show flows for this device');
}
function addPortRow(tbody, port) {
@@ -146,14 +168,15 @@
}
function populateDetails(details) {
- var topTb, btmTbl, ports;
+ var topTbs, btnsDiv, btmTbl, ports;
setUpPanel();
- topTb = top.select('table').append('tbody');
+ topTbs = top.select('.top-tables');
+ btnsDiv = top.select('.actionBtns');
btmTbl = bottom.select('table');
ports = details.ports;
- populateTop(topTb, details);
+ populateTop(topTbs, btnsDiv, details);
populateBottom(btmTbl, ports);
detailsPanel.height(pHeight);
@@ -182,8 +205,10 @@
.controller('OvDeviceCtrl',
['$log', '$scope', 'TableBuilderService', 'FnService',
'MastService', 'PanelService', 'WebSocketService', 'IconService',
+ 'ButtonService', 'NavService', 'TooltipService',
- function (_$log_, _$scope_, tbs, _fs_, _mast_, _ps_, _wss_, _is_) {
+ function (_$log_, _$scope_,
+ tbs, _fs_, _mast_, _ps_, _wss_, _is_, _bns_, _ns_, _ttip_) {
$log = _$log_;
$scope = _$scope_;
fs = _fs_;
@@ -191,6 +216,9 @@
ps = _ps_;
wss = _wss_;
is = _is_;
+ bns = _bns_;
+ ns = _ns_;
+ ttip = _ttip_;
self = this;
var handlers = {};
self.panelData = [];
@@ -217,12 +245,14 @@
});
createDetailsPane();
+ // details panel handlers
handlers[detailsResp] = respDetailsCb;
wss.bindHandlers(handlers);
$scope.$on('$destroy', function () {
ps.destroyPanel(pName);
wss.unbindHandlers(handlers);
+ ttip.resetTooltip();
});
$log.log('OvDeviceCtrl has been created');
diff --git a/web/gui/src/main/webapp/app/view/topo/topo.css b/web/gui/src/main/webapp/app/view/topo/topo.css
index d65df90..9356c60 100644
--- a/web/gui/src/main/webapp/app/view/topo/topo.css
+++ b/web/gui/src/main/webapp/app/view/topo/topo.css
@@ -85,9 +85,6 @@
top: 320px;
}
-#topo-p-detail .actionBtns {
- text-align: center;
-}
#topo-p-detail .actionBtns .actionBtn {
display: inline-block;
}
diff --git a/web/gui/src/main/webapp/app/view/topo/topo.js b/web/gui/src/main/webapp/app/view/topo/topo.js
index 68e4d68..8479823 100644
--- a/web/gui/src/main/webapp/app/view/topo/topo.js
+++ b/web/gui/src/main/webapp/app/view/topo/topo.js
@@ -30,7 +30,7 @@
// references to injected services etc.
var $log, $cookies, fs, ks, zs, gs, ms, sus, flash, wss, ps,
- tes, tfs, tps, tis, tss, tls, tts, tos, fltr, ttbs;
+ tes, tfs, tps, tis, tss, tls, tts, tos, fltr, ttbs, ttip;
// DOM elements
var ovtopo, svg, defs, zoomLayer, mapG, spriteG, forceG, noDevsLayer;
@@ -319,11 +319,12 @@
'TopoEventService', 'TopoForceService', 'TopoPanelService',
'TopoInstService', 'TopoSelectService', 'TopoLinkService',
'TopoTrafficService', 'TopoObliqueService', 'TopoFilterService',
- 'TopoToolbarService', 'TopoSpriteService',
+ 'TopoToolbarService', 'TopoSpriteService', 'TooltipService',
function ($scope, _$log_, $loc, $timeout, _$cookies_, _fs_, mast, _ks_,
_zs_, _gs_, _ms_, _sus_, _flash_, _wss_, _ps_, _tes_, _tfs_,
- _tps_, _tis_, _tss_, _tls_, _tts_, _tos_, _fltr_, _ttbs_, tspr) {
+ _tps_, _tis_, _tss_, _tls_, _tts_, _tos_, _fltr_, _ttbs_, tspr,
+ _ttip_) {
var self = this,
projection,
dim,
@@ -360,6 +361,7 @@
tos = _tos_;
fltr = _fltr_;
ttbs = _ttbs_;
+ ttip = _ttip_;
self.notifyResize = function () {
svgResized(fs.windowSize(mast.mastHeight()));
@@ -373,6 +375,7 @@
tis.destroyInst();
tfs.destroyForce();
ttbs.destroyToolbar();
+ ttip.resetTooltip();
});
// svg layer and initialization of components
diff --git a/web/gui/src/main/webapp/app/view/topo/topoSelect.js b/web/gui/src/main/webapp/app/view/topo/topoSelect.js
index 44fd14a..cf08d2f 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoSelect.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoSelect.js
@@ -23,7 +23,7 @@
'use strict';
// injected refs
- var $log, fs, wss, tps, tts;
+ var $log, fs, wss, tps, tts, ns;
// api to topoForce
var api;
@@ -40,6 +40,9 @@
selectOrder = [], // the order in which we made selections
consumeClick = false; // used to coordinate with SVG click handler
+ // constants
+ var flowPath = 'flow';
+
// ==========================
function nSel() {
@@ -240,6 +243,18 @@
tt: 'Show Device Flows'
});
}
+ // TODO: have the server return explicit class and ID of each node
+ // for now, we assume the node is a device if it has a URI
+ if ((data.props).hasOwnProperty('URI')) {
+ tps.addAction({
+ id: 'flows-table-btn',
+ gid: 'flowsTable',
+ cb: function () {
+ ns.navTo(flowPath, { devId: data.id });
+ },
+ tt: 'Show flows for this device'
+ });
+ }
tps.displaySomething();
}
@@ -264,14 +279,15 @@
angular.module('ovTopo')
.factory('TopoSelectService',
['$log', 'FnService', 'WebSocketService',
- 'TopoPanelService', 'TopoTrafficService',
+ 'TopoPanelService', 'TopoTrafficService', 'NavService',
- function (_$log_, _fs_, _wss_, _tps_, _tts_) {
+ function (_$log_, _fs_, _wss_, _tps_, _tts_, _ns_) {
$log = _$log_;
fs = _fs_;
wss = _wss_;
tps = _tps_;
tts = _tts_;
+ ns = _ns_;
function initSelect(_api_) {
api = _api_;
diff --git a/web/gui/src/main/webapp/tests/app/fw/mast/mast-spec.js b/web/gui/src/main/webapp/tests/app/fw/mast/mast-spec.js
index 67fbfbb..26ccef8 100644
--- a/web/gui/src/main/webapp/tests/app/fw/mast/mast-spec.js
+++ b/web/gui/src/main/webapp/tests/app/fw/mast/mast-spec.js
@@ -19,21 +19,18 @@
*/
describe('Controller: MastCtrl', function () {
// instantiate the masthead module
- beforeEach(module('onosMast'));
+ beforeEach(module('onosMast', 'onosUtil'));
- var $log, ctrl, ms;
+ var $log, ctrl, ms, fs;
// we need an instance of the controller
- beforeEach(inject(function(_$log_, $controller, MastService) {
+ beforeEach(inject(function(_$log_, $controller, MastService, FnService) {
$log = _$log_;
ctrl = $controller('MastCtrl');
ms = MastService;
+ fs = FnService;
}));
- it('should start with no radio buttons', function () {
- expect(ctrl.radio).toBeNull();
- });
-
it('should declare height to be 36', function () {
expect(ms.mastHeight()).toBe(36);
})
diff --git a/web/gui/src/main/webapp/tests/app/fw/nav/nav-spec.js b/web/gui/src/main/webapp/tests/app/fw/nav/nav-spec.js
index 34281ef..d14d514 100644
--- a/web/gui/src/main/webapp/tests/app/fw/nav/nav-spec.js
+++ b/web/gui/src/main/webapp/tests/app/fw/nav/nav-spec.js
@@ -18,14 +18,29 @@
ONOS GUI -- Util -- Theme Service - Unit Tests
*/
describe('factory: fw/nav/nav.js', function() {
- var ns, $log, fs;
+ var $log, $location, $window, ns, fs;
var d3Elem;
beforeEach(module('onosNav', 'onosUtil'));
- beforeEach(inject(function (NavService, _$log_, FnService) {
- ns = NavService;
+ var mockWindow = {
+ location: {
+ href: 'http://server/#/mock/url'
+ }
+ };
+
+ beforeEach(function () {
+ module(function ($provide) {
+ $provide.value('$window', mockWindow);
+ });
+ });
+
+ beforeEach(inject(function (_$log_, _$location_, _$window_,
+ NavService, FnService) {
$log = _$log_;
+ $location = _$location_;
+ $window = _$window_;
+ ns = NavService;
fs = FnService;
d3Elem = d3.select('body').append('div').attr('id', 'nav');
ns.hideNav();
@@ -41,7 +56,7 @@
it('should define api functions', function () {
expect(fs.areFunctions(ns, [
- 'showNav', 'hideNav', 'toggleNav', 'hideIfShown'
+ 'showNav', 'hideNav', 'toggleNav', 'hideIfShown', 'navTo'
])).toBeTruthy();
});
@@ -95,4 +110,56 @@
checkHidden(true);
});
+ it('should take correct navTo parameters', function () {
+ spyOn($log, 'warn');
+
+ ns.navTo('foo');
+ expect($log.warn).not.toHaveBeenCalled();
+
+ ns.navTo('bar', { q1: 'thing', q2: 'thing2' });
+ expect($log.warn).not.toHaveBeenCalled();
+
+ });
+
+ it('should check navTo parameter warnings', function () {
+ spyOn($log, 'warn');
+
+ expect(ns.navTo()).toBeNull();
+ expect($log.warn).toHaveBeenCalledWith('Not a valid navigation path');
+
+ ns.navTo('baz', [1, 2, 3]);
+ expect($log.warn).toHaveBeenCalledWith(
+ 'Query params not an object', [1, 2, 3]
+ );
+
+ ns.navTo('zoom', 'not a query param');
+ expect($log.warn).toHaveBeenCalledWith(
+ 'Query params not an object', 'not a query param'
+ );
+ });
+
+ it('should verify where the window is navigating', function () {
+ ns.navTo('foo');
+ expect($window.location.href).toBe('http://server/#/foo');
+
+ ns.navTo('bar');
+ expect($window.location.href).toBe('http://server/#/bar');
+
+ ns.navTo('baz', { q1: 'thing1', q2: 'thing2' });
+ expect($window.location.href).toBe(
+ 'http://server/#/baz?q1=thing1&q2=thing2'
+ );
+
+ ns.navTo('zip', { q3: 'thing3' });
+ expect($window.location.href).toBe(
+ 'http://server/#/zip?q3=thing3'
+ );
+
+ ns.navTo('zoom', {});
+ expect($window.location.href).toBe('http://server/#/zoom');
+
+ ns.navTo('roof', [1, 2, 3]);
+ expect($window.location.href).toBe('http://server/#/roof');
+ });
+
});
diff --git a/web/gui/src/main/webapp/tests/app/fw/widget/tooltip-spec.js b/web/gui/src/main/webapp/tests/app/fw/widget/tooltip-spec.js
index 0ae1f65..165b51d 100644
--- a/web/gui/src/main/webapp/tests/app/fw/widget/tooltip-spec.js
+++ b/web/gui/src/main/webapp/tests/app/fw/widget/tooltip-spec.js
@@ -42,7 +42,7 @@
it('should define api functions', function () {
expect(fs.areFunctions(tts, [
- 'showTooltip', 'cancelTooltip'
+ 'showTooltip', 'cancelTooltip', 'resetTooltip'
])).toBeTruthy();
});