Bri Prebilic Cole

GUI -- Buttons added to topo and device views that navigate to new flows table view.

Change-Id: Ibea4415d3c1fc717e609aebcd2205d0bba01c96d
......@@ -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',
function (_$log_, _$location_, _$window_, _fs_) {
$log = _$log_;
$location = _$location_;
$window = _$window_;
fs = _fs_;
return {
showNav: showNav,
hideNav: hideNav,
toggleNav: toggleNav,
hideIfShown: hideIfShown
};
return {
showNav: showNav,
hideNav: hideNav,
toggleNav: toggleNav,
hideIfShown: hideIfShown,
navTo: navTo
};
}]);
}());
......
......@@ -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 @@
}
}
function resetTooltip() {
tooltip.style('display', 'none').text('');
}
angular.module('onosWidget')
.factory('TooltipService', ['$log', '$timeout', 'FnService',
.factory('TooltipService', ['$log', 'FnService',
function (_$log_, _$timeout_, _fs_) {
function (_$log_, _fs_) {
$log = _$log_;
$timeout = _$timeout_;
fs = _fs_;
init();
return {
showTooltip: showTooltip,
cancelTooltip: cancelTooltip
cancelTooltip: cancelTooltip,
resetTooltip: resetTooltip
};
}]);
}());
......
......@@ -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 {
......
......@@ -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');
......
......@@ -85,9 +85,6 @@
top: 320px;
}
#topo-p-detail .actionBtns {
text-align: center;
}
#topo-p-detail .actionBtns .actionBtn {
display: inline-block;
}
......
......@@ -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
......
......@@ -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_;
......
......@@ -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);
})
......
......@@ -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 @@ describe('factory: fw/nav/nav.js', function() {
it('should define api functions', function () {
expect(fs.areFunctions(ns, [
'showNav', 'hideNav', 'toggleNav', 'hideIfShown'
'showNav', 'hideNav', 'toggleNav', 'hideIfShown', 'navTo'
])).toBeTruthy();
});
......@@ -95,4 +110,56 @@ describe('factory: fw/nav/nav.js', function() {
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');
});
});
......
......@@ -42,7 +42,7 @@ describe('factory: fw/widget/tooltip.js', function () {
it('should define api functions', function () {
expect(fs.areFunctions(tts, [
'showTooltip', 'cancelTooltip'
'showTooltip', 'cancelTooltip', 'resetTooltip'
])).toBeTruthy();
});
......