angular-based help-panel widget

The help-panel will be implemented as a reusable widget so it can
be used elsewhere.

Design:
* Angular-based.
* Easily usable anywhere in Horizon.

Change-Id: If4be6af5f4c868d95783a9a9c846f70b09036ce4
Partially Implements: blueprint initial-reusable-angular-wizard
This commit is contained in:
Thai Tran 2015-01-28 18:13:26 -08:00 committed by Richard Jones
parent 1bff6943a7
commit 22a2129eb2
13 changed files with 214 additions and 5 deletions

View File

@ -0,0 +1,5 @@
<div class="help-panel" ng-class="{'open': openHelp}">
<button class="open" ng-click="openHelp=true"><span class="fa fa-question-circle"></span></button>
<button class="close" ng-click="openHelp=false"><span class="fa fa-times-circle"></span></button>
<div class="content" ng-transclude></div>
</div>

View File

@ -0,0 +1,13 @@
(function () {
'use strict';
angular.module('hz.widget.help-panel', [])
.directive('helpPanel', ['basePath',
function (path) {
return {
templateUrl: path + 'help-panel/help-panel.html',
transclude: true
};
}
]);
})();

View File

@ -0,0 +1,74 @@
.help-panel {
position: absolute;
width: $helpPanelWidthDefault;
right: -$helpPanelWidthDefault;
top: 0;
bottom: 0;
color: $helpPanelColor;
background: $helpPanelBg;
-webkit-transition: right linear 0.1s;
transition: right linear 0.1s;
z-index: 10;
.content {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
padding: 10px 20px;
overflow-y: auto;
h1 {
font-size: 20px;
line-height: 1.8;
margin: 0;
}
p {
line-height: 1.4;
margin: 1em 0;
}
}
&.open {
right: 0;
& > button.open {
display: none;
}
& > button.close {
display: block;
opacity: 1; // override bootstrap
font-size: 14px; // override bootstrap
}
}
& > button {
position: absolute;
top: 0;
left: -$helpPanelBtnSize;
width: $helpPanelBtnSize;
height: $helpPanelBtnSize;
line-height: $helpPanelBtnSize;
padding: 0;
border: none;
text-align: center;
vertical-align: middle;
background: $helpPanelBg;
// button icon
& > * {
display: inline-block;
vertical-align: middle;
background: $helpPanelBtnBg;
color: $helpPanelBtnColor;
font-size: $helpPanelBtnIconSize;
}
&.close {
display: none;
}
}
}

View File

@ -0,0 +1,50 @@
'use strict';
describe('hz.widget.help-panel module', function() {
it('should have been defined".', function () {
expect(angular.module('hz.widget.help-panel')).toBeDefined();
});
});
describe('help-panel directive', function () {
var $compile,
$scope;
beforeEach(module('templates'));
beforeEach(module('hz'));
beforeEach(module('hz.widgets'));
beforeEach(module('hz.widget.help-panel'));
beforeEach(inject(function ($injector) {
$scope = $injector.get('$rootScope').$new();
$compile = $injector.get('$compile');
}));
it('should be compiled', function () {
var element = $compile('<help-panel>Help</help-panel>')($scope);
$scope.$digest();
expect(element.html().trim()).not.toBe('Help');
expect(element.text().trim()).toBe('Help');
});
it('should be closed by default', function () {
var element = $compile('<help-panel>Help</help-panel>')($scope);
$scope.$digest();
expect(element[0].querySelector('.help-panel').className).toBe('help-panel');
});
it('should add "open" to class name if $scope.openHelp===true', function () {
var element = $compile('<help-panel>Help</help-panel>')($scope);
$scope.openHelp = true;
$scope.$digest();
expect(element[0].querySelector('.help-panel').className).toBe('help-panel open');
});
it('should remove "open" from class name if $scope.openHelp===false', function () {
var element = $compile('<help-panel>Help</help-panel>')($scope);
$scope.openHelp = true;
$scope.$digest();
$scope.openHelp = false;
$scope.$digest();
expect(element[0].querySelector('.help-panel').className).toBe('help-panel');
});
});

View File

@ -0,0 +1 @@
@import "help-panel/help-panel";

View File

@ -0,0 +1,9 @@
(function () {
'use strict';
angular.module('hz.widgets', [
'hz.widget.help-panel'
])
.constant('basePath', '/static/angular/');
})();

View File

@ -2,7 +2,7 @@
(function () {
'use strict';
var horizon_dependencies = ['hz.conf', 'hz.utils', 'ngCookies'];
var horizon_dependencies = ['hz.conf', 'hz.utils', 'ngCookies', 'hz.widgets'];
var dependencies = horizon_dependencies.concat(angularModuleExtension);
angular.module('hz', dependencies)
.config(['$interpolateProvider', '$httpProvider',
@ -28,4 +28,4 @@
});
};
}]);
}());
}());

View File

@ -21,6 +21,9 @@
<script src='{{ STATIC_URL }}horizon/js/angular/services/horizon.utils.js' type='text/javascript' charset='utf-8'></script>
<script src='{{ STATIC_URL }}horizon/js/angular/controllers/metadata-widget-controller.js'></script>
<script src='{{ STATIC_URL }}angular/widget.module.js'></script>
<script src='{{ STATIC_URL }}angular/help-panel/help-panel.js'></script>
<script src='{{ STATIC_URL }}horizon/lib/jquery/jquery.quicksearch.js' type='text/javascript' charset="utf-8"></script>
<script src="{{ STATIC_URL }}horizon/lib/jquery/jquery.tablesorter.js" type="text/javascript" charset="utf-8"></script>
<script src="{{ STATIC_URL }}horizon/lib/spin.js" type="text/javascript" charset="utf-8"></script>

View File

@ -32,5 +32,37 @@
{% endblock %}
</div>
<script type="text/javascript">
(function () { 'use strict';
// Caching all external angular templates
var templates = [
{% for externalTemplate in externalTemplates %}
{{ STATIC_URL }}{{ externalTemplate }} + ","
{% endfor %}
];
var tplmodule = angular.module('templates', []);
templates.forEach(function (template) {
cacheTemplate(template, tplmodule);
});
function cacheTemplate(template, tplmodule) {
tplmodule.run(function ($templateCache) {
$templateCache.put(template, loadSync(template));
});
}
function loadSync(url) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, false);
xhr.send();
return xhr.status === 200 ? xhr.responseText : null;
}
})();
</script>
</body>
</html>

View File

@ -41,6 +41,7 @@ def dispatcher(request, test_name):
return django.shortcuts.render(
request,
template,
{'specs': cls.specs, 'sources': cls.sources})
{'specs': cls.specs, 'sources': cls.sources,
'externalTemplates': cls.externalTemplates})
return django.views.defaults.page_not_found(request)

View File

@ -22,9 +22,15 @@ class ServicesTests(test.JasmineTests):
'horizon/js/angular/horizon.conf.js',
'horizon/js/angular/horizon.js',
'horizon/js/angular/services/horizon.utils.js',
'horizon/js/angular/controllers/metadata-widget-controller.js'
'horizon/js/angular/controllers/metadata-widget-controller.js',
'angular/widget.module.js',
'angular/help-panel/help-panel.js'
]
specs = [
'horizon/tests/jasmine/utilsSpec.js',
'horizon/tests/jasmine/metadataWidgetControllerSpec.js'
'horizon/tests/jasmine/metadataWidgetControllerSpec.js',
'angular/help-panel/help-panel.spec.js'
]
externalTemplates = [
'angular/help-panel/help-panel.html'
]

View File

@ -70,3 +70,17 @@ $overview_chart_height: 81px;
/* Accordion Navigation */
$accordionBorderColor: #e5e5e5;
/* Help panel */
// theme
$helpPanelColor: #333 !default;
$helpPanelBg: #eee !default;
$helpPanelBtnColor: #333 !default;
$helpPanelBtnBg: transparent !default;
// layout
$helpPanelWidthDefault: 300px !default;
$helpPanelBtnSize: 40px !default;
$helpPanelBtnIconSize: 28px !default;

View File

@ -17,6 +17,7 @@
@import "components/charts";
@import "components/workflow";
@import "components/network_topology";
@import "/angular/styles";
/* new clearfix */