Created common navigation header
This patch adds the nodes/drivers navigation header in accordance to the approved UI specification. The state chart had to be adjusted to make room for the driver section, and a new common ironic root state has been introduced to ensure that the menu remains constant. Change-Id: I57fe522a067d515aa5253888ef6c121c4b95bd61
This commit is contained in:
parent
7a3e960b37
commit
a4798afe90
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
@import "./bootstrap_variables";
|
@import "./bootstrap_variables";
|
||||||
@import "../../bower_components/bootstrap-sass/assets/stylesheets/bootstrap";
|
@import "../../bower_components/bootstrap-sass/assets/stylesheets/bootstrap";
|
||||||
@import "./bootstrap_mixins";
|
@import "./bootstrap_mixins";
|
||||||
|
@ -21,6 +20,17 @@
|
||||||
@include box-shadow($shadow);
|
@include box-shadow($shadow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Button default should have button primary active state.
|
||||||
|
*/
|
||||||
|
.btn-default {
|
||||||
|
&.active, &.active:active, &.active:focus, &.active:hover {
|
||||||
|
background-color: $brand-primary;
|
||||||
|
color: $btn-primary-color;
|
||||||
|
border-color: $btn-primary-border;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom button gradient styles.
|
* Custom button gradient styles.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
<html ng-app="ironic">
|
<html ng-app="ironic">
|
||||||
<head>
|
<head>
|
||||||
<title>OpenStack Bare Metal</title>
|
<title>OpenStack Bare Metal</title>
|
||||||
|
|
|
@ -48,7 +48,7 @@ angular.module('ironic').controller('ConfigurationController',
|
||||||
vm.select = function(configuration) {
|
vm.select = function(configuration) {
|
||||||
$$selectedConfiguration.set(configuration).$promise.then(
|
$$selectedConfiguration.set(configuration).$promise.then(
|
||||||
function() {
|
function() {
|
||||||
$state.go('root.ironic');
|
$state.go('root.ironic.nodes');
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -60,4 +60,17 @@ angular.module('ironic').controller('NodeActionController',
|
||||||
}
|
}
|
||||||
}).result;
|
}).result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enroll a new node.
|
||||||
|
*
|
||||||
|
* @return {Promise} A promise that will resolve true if a node has been added.
|
||||||
|
*/
|
||||||
|
vm.enroll = function() {
|
||||||
|
return $uibModal.open({
|
||||||
|
templateUrl: 'view/ironic/enroll/index.html',
|
||||||
|
controller: 'EnrollModalController as ctrl',
|
||||||
|
backdrop: 'static'
|
||||||
|
}).result;
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -49,13 +49,5 @@ angular.module('ironic')
|
||||||
$log.info('Set power state on ' + node.uuid + ' to ' + stateName);
|
$log.info('Set power state on ' + node.uuid + ' to ' + stateName);
|
||||||
};
|
};
|
||||||
|
|
||||||
vm.enroll = function() {
|
|
||||||
$uibModal.open({
|
|
||||||
templateUrl: 'view/ironic/enroll/index.html',
|
|
||||||
controller: 'EnrollModalController as ctrl',
|
|
||||||
backdrop: 'static'
|
|
||||||
}).result.then(vm.loadNodes);
|
|
||||||
};
|
|
||||||
|
|
||||||
vm.loadNodes();
|
vm.loadNodes();
|
||||||
});
|
});
|
||||||
|
|
|
@ -25,7 +25,7 @@ angular.module('ironic', ['ui.router', 'ui.bootstrap', 'ironic.util', 'ironic.ap
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// Default UI route
|
// Default UI route
|
||||||
$urlRouterProvider.otherwise('/ironic');
|
$urlRouterProvider.otherwise('/ironic/nodes');
|
||||||
|
|
||||||
// Enable all of our configuration detection methods
|
// Enable all of our configuration detection methods
|
||||||
$$configurationProvider.$enableDefault(true);
|
$$configurationProvider.$enableDefault(true);
|
||||||
|
@ -38,18 +38,18 @@ angular.module('ironic', ['ui.router', 'ui.bootstrap', 'ironic.util', 'ironic.ap
|
||||||
.state('root', {
|
.state('root', {
|
||||||
abstract: true,
|
abstract: true,
|
||||||
url: '',
|
url: '',
|
||||||
templateUrl: 'view/ironic/index.html'
|
templateUrl: 'view/ironic/root.html'
|
||||||
})
|
})
|
||||||
.state('root.ironic', {
|
.state('root.ironic', {
|
||||||
|
abstract: true,
|
||||||
url: '/ironic',
|
url: '/ironic',
|
||||||
views: {
|
views: {
|
||||||
header: {
|
header: {
|
||||||
templateUrl: 'view/ironic/header.html',
|
templateUrl: 'view/ironic/header.html',
|
||||||
controller: 'HeaderController as headerCtrl'
|
controller: 'HeaderController as headerCtrl'
|
||||||
},
|
},
|
||||||
main: {
|
root: {
|
||||||
templateUrl: 'view/ironic/node_list.html',
|
templateUrl: 'view/ironic/ironic.html'
|
||||||
controller: 'NodeListController as nodeListCtrl'
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
|
@ -72,8 +72,13 @@ angular.module('ironic', ['ui.router', 'ui.bootstrap', 'ironic.util', 'ironic.ap
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.state('root.ironic.nodes', {
|
.state('root.ironic.nodes', {
|
||||||
abstract: true,
|
url: '/nodes',
|
||||||
url: '/nodes'
|
views: {
|
||||||
|
'main@root.ironic': {
|
||||||
|
templateUrl: 'view/ironic/node_list.html',
|
||||||
|
controller: 'NodeListController as nodeListCtrl'
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.state('root.ironic.nodes.detail', {
|
.state('root.ironic.nodes.detail', {
|
||||||
abstract: true,
|
abstract: true,
|
||||||
|
@ -84,7 +89,7 @@ angular.module('ironic', ['ui.router', 'ui.bootstrap', 'ironic.util', 'ironic.ap
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
views: {
|
views: {
|
||||||
'main@root': {
|
'main@root.ironic': {
|
||||||
templateUrl: 'view/ironic/detail.html',
|
templateUrl: 'view/ironic/detail.html',
|
||||||
controller: 'NodeDetailController as nodeCtrl'
|
controller: 'NodeDetailController as nodeCtrl'
|
||||||
}
|
}
|
||||||
|
@ -104,13 +109,21 @@ angular.module('ironic', ['ui.router', 'ui.bootstrap', 'ironic.util', 'ironic.ap
|
||||||
templateUrl: 'view/ironic/detail_driver.html',
|
templateUrl: 'view/ironic/detail_driver.html',
|
||||||
controller: 'NodeDetailDriverController as driverCtrl'
|
controller: 'NodeDetailDriverController as driverCtrl'
|
||||||
})
|
})
|
||||||
|
.state('root.ironic.drivers', {
|
||||||
|
url: '/drivers',
|
||||||
|
views: {
|
||||||
|
'main@root.ironic': {
|
||||||
|
templateUrl: 'view/ironic/driver_list.html'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
.state('root.config', {
|
.state('root.config', {
|
||||||
url: '/config',
|
url: '/config',
|
||||||
views: {
|
views: {
|
||||||
header: {
|
header: {
|
||||||
templateUrl: 'view/ironic/config_header.html'
|
templateUrl: 'view/ironic/config_header.html'
|
||||||
},
|
},
|
||||||
main: {
|
root: {
|
||||||
templateUrl: 'view/ironic/config.html',
|
templateUrl: 'view/ironic/config.html',
|
||||||
controller: 'ConfigurationController as ctrl'
|
controller: 'ConfigurationController as ctrl'
|
||||||
}
|
}
|
||||||
|
@ -125,7 +138,7 @@ angular.module('ironic', ['ui.router', 'ui.bootstrap', 'ironic.util', 'ironic.ap
|
||||||
if (reason === 'no_config') {
|
if (reason === 'no_config') {
|
||||||
$state.go('root.config');
|
$state.go('root.config');
|
||||||
} else {
|
} else {
|
||||||
$state.go('root.ironic');
|
$state.go('root.ironic.nodes');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
$rootScope.$on('$destroy', listener);
|
$rootScope.$on('$destroy', listener);
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12">
|
||||||
|
<h1>Driver List</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12">
|
||||||
|
Not yet implemented.
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,22 @@
|
||||||
|
<div class="row" ng-controller="NodeActionController as ctrl">
|
||||||
|
<div class="col-xs-6">
|
||||||
|
<div class="btn-group btn-group-lg">
|
||||||
|
<button type="button"
|
||||||
|
active-path="\/ironic\/nodes\/?.*"
|
||||||
|
ui-sref="root.ironic.nodes"
|
||||||
|
class="btn btn-default">Nodes</button>
|
||||||
|
<button type="button"
|
||||||
|
ui-sref="root.ironic.drivers"
|
||||||
|
active-path="\/ironic\/drivers\/?.*"
|
||||||
|
class="btn btn-default">Drivers</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-xs-6">
|
||||||
|
<button ng-click="ctrl.enroll()"
|
||||||
|
class="btn btn-primary btn-lg pull-right">
|
||||||
|
<i class="fa fa-plus"></i> Node
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div ui-view="main">
|
||||||
|
</div>
|
|
@ -1,9 +1,5 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-12">
|
<div class="col-xs-12">
|
||||||
<button ng-click="nodeListCtrl.enroll()" class="btn btn-default pull-right-bottom">
|
|
||||||
<i class="fa fa-gear"></i>
|
|
||||||
Enroll Node
|
|
||||||
</button>
|
|
||||||
<h1>Nodes</h1>
|
<h1>Nodes</h1>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<div class="container-fluid" ui-view="header"></div>
|
<div class="container-fluid" ui-view="header"></div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div class="container-fluid" ui-view="main"></div>
|
<div class="container-fluid" ui-view="root"></div>
|
||||||
|
|
||||||
<nav class="navbar navbar-default navbar-fixed-bottom">
|
<nav class="navbar navbar-default navbar-fixed-bottom">
|
||||||
<div class="container-fluid" ui-view="footer"></div>
|
<div class="container-fluid" ui-view="footer"></div>
|
|
@ -113,4 +113,22 @@ describe('Unit: Ironic-webclient NodeActionController',
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('$scope.enroll', function() {
|
||||||
|
var enrollInjectionProperties = angular.copy(mockInjectionProperties);
|
||||||
|
enrollInjectionProperties.$uibModal = {
|
||||||
|
open: function() {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should open a modal',
|
||||||
|
inject(function($q) {
|
||||||
|
var spy = spyOn(enrollInjectionProperties.$uibModal, 'open').and.callFake(function() {
|
||||||
|
return {result: $q.resolve({})};
|
||||||
|
});
|
||||||
|
var controller = $controller('NodeActionController', enrollInjectionProperties);
|
||||||
|
controller.enroll();
|
||||||
|
|
||||||
|
expect(spy.calls.count()).toBe(1);
|
||||||
|
}));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -106,41 +106,4 @@ describe('Unit: Ironic-webclient node list controller',
|
||||||
expect(controller.nodes).toBeFalsy();
|
expect(controller.nodes).toBeFalsy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('$scope.enroll', function() {
|
|
||||||
it('should open a modal',
|
|
||||||
inject(function($q) {
|
|
||||||
var spy = spyOn(mockInjectionProperties.$uibModal, 'open').and.callFake(function() {
|
|
||||||
return {result: $q.resolve({})};
|
|
||||||
});
|
|
||||||
var controller = $controller('NodeListController', mockInjectionProperties);
|
|
||||||
controller.enroll();
|
|
||||||
$httpBackend.flush();
|
|
||||||
|
|
||||||
expect(spy.calls.count()).toBe(1);
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should reload the node list if a new node was added.',
|
|
||||||
inject(function($q) {
|
|
||||||
// Set up a spy.
|
|
||||||
var spy = spyOn(mockInjectionProperties.$uibModal, 'open').and.callFake(function() {
|
|
||||||
return {result: $q.resolve({})};
|
|
||||||
});
|
|
||||||
|
|
||||||
// Initialize the controller.
|
|
||||||
var controller = $controller('NodeListController', mockInjectionProperties);
|
|
||||||
$httpBackend.flush();
|
|
||||||
|
|
||||||
// Setup expected calls
|
|
||||||
$httpBackend
|
|
||||||
.expectGET('http://ironic.example.com:1000/nodes')
|
|
||||||
.respond(200, []);
|
|
||||||
|
|
||||||
// Call Enroll
|
|
||||||
controller.enroll();
|
|
||||||
$httpBackend.flush();
|
|
||||||
|
|
||||||
expect(spy.calls.count()).toBe(1);
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue