Angular Confirmation Modal

We need a simple dialog for confirming dangerous actions such as delete.
This patch introduces a simple wrapper that allows user to pass in:
1. Title - title of the dialog
2. Body - simple text or html (not a template!)
3. Submit label
4. Cancel label

Change-Id: Ibe019bf8a2d08c6323ebb6e39bfac7dcac8852c6
Partially-Implements: blueprint angularize-identity-tables
This commit is contained in:
Thai Tran 2015-02-11 12:39:24 -08:00 committed by Richard Jones
parent 0a85d5ebb0
commit 7cb9efa4fd
7 changed files with 176 additions and 4 deletions

95
horizon/static/angular/modal/modal.js vendored Normal file
View File

@ -0,0 +1,95 @@
(function() {
'use strict';
/**
* @ngdoc overview
* @name hz.widget.modal
*
* # hz.widget.modal
*
* The `hz.widget.modal` provides modal services.
*
* Requires {@link http://angular-ui.github.io/bootstrap/ `Angular-bootstrap`}
*
* | Components |
* |--------------------------------------------------------------------------|
* | {@link hz.widget.modal.controller:simpleModalCtrl `simpleModalCtrl`} |
* | {@link hz.widget.modal.factory:simpleModalService `simpleModalService`} |
*
*/
angular.module('hz.widget.modal', ['ui.bootstrap'])
/**
* @ngdoc controller
* @name simpleModalCtrl
*
* @param(object) scope of the controller
* @param(object) modal instance from angular-bootstrap
* @param(object) context object provided by the user
*
* @description
* Horizon's controller for confirmation dialog.
* Passes context along to the template.
* If user presses cancel button or closes dialog, modal gets dismissed.
* If user presses submit button, modal gets closed.
* This controller is automatically included in modalService.
*/
.controller('simpleModalCtrl', [ '$scope', '$modalInstance', 'context',
function($scope, $modalInstance, context) {
$scope.context = context;
$scope.submit = function(){ $modalInstance.close(); };
$scope.cancel = function(){ $modalInstance.dismiss('cancel'); };
} // end of function
]) // end of controller
/**
* @ngdoc service
* @name simpleModalService
*
* @description
* Horizon's wrapper for angular-bootstrap modal service.
* It should only be use for small confirmation dialogs.
* @param {object} the object containing title, body, submit, and cancel labels
* @param {object} the object returned from angular-bootstrap $modal
*
* @example:
* angular.controller('modalExampleCtrl', [ '$scope', 'simpleModalService',
* function($scope, simpleModalService){
* var options = {
* title: 'Confirm Delete',
* body: 'Are you sure you want to delete this item?',
* submit: 'Yes',
* cancel: 'No',
* };
* simpleModalService(options).result.then(function(){
* // user clicked on submit button
* // do something useful here
* });
* }
* ]);
*/
.factory('simpleModalService', ['$modal', 'basePath',
function($modal, path) {
return function(params) {
if (params && params.title && params.body){
var options = {
controller: 'simpleModalCtrl',
templateUrl: path + 'modal/simple-modal.html',
resolve: {
context: function() {
return {
title: params.title? params.title: '',
body: params.body? params.body: '',
submit: params.submit? params.submit: gettext('Submit'),
cancel: gettext('Cancel')
};
}
}
};
return $modal.open(options);
}
}; // end of return
} // end of function
]); // end of factory
})();

View File

@ -0,0 +1,18 @@
<div class="modal-header">
<h3 class="modal-title">
<span ng-bind="::context.title"></span>
</h3>
</div>
<div class="modal-body clearfix">
<p ng-bind="::context.body"></p>
</div>
<div class="modal-footer">
<button class="btn btn-default secondary"
type="button" ng-click="cancel()">
<span ng-bind="::context.cancel"></span>
</button>
<button class="btn btn-primary"
type="button" ng-click="submit()">
<span ng-bind="::context.submit"></span>
</button>
</div>

View File

@ -0,0 +1,55 @@
'use strict';
describe('hz.widget.modal module', function(){
it('should have been defined', function(){
expect(angular.module('hz.widget.modal')).toBeDefined();
});
});
describe('modal factory', function(){
var modal;
var modalData = {
title: 'Confirm deletion',
body: 'Are you sure?',
submit: 'Yes',
cancel: 'No'
};
beforeEach(module('ui.bootstrap'));
beforeEach(module('hz'));
beforeEach(module('hz.widgets'));
beforeEach(module('hz.widget.modal'));
beforeEach(inject(function($injector, simpleModalService){
modal = simpleModalService;
}));
it('should fail without any params', function(){
expect(modal()).toBeUndefined();
});
it('should fail without a title', function(){
var data = angular.copy(modalData);
delete data.title;
expect(modal(data)).toBeUndefined();
});
it('should fail without a body', function(){
var data = angular.copy(modalData);
delete data.body;
expect(modal(data)).toBeUndefined();
});
it('should have default submit and cancel labels', function(){
var data = angular.copy(modalData);
delete data.submit;
delete data.cancel;
expect(modal(data)).toBeDefined();
});
it('should work when all params are supplied', function(){
var data = angular.copy(modalData);
expect(modal(data)).toBeDefined();
});
});

View File

@ -4,7 +4,8 @@
angular.module('hz.widgets', [
'hz.widget.help-panel',
'hz.widget.wizard',
'hz.widget.table'
'hz.widget.table',
'hz.widget.modal'
])
.constant('basePath', '/static/angular/');

View File

@ -26,6 +26,7 @@
<script src='{{ STATIC_URL }}angular/help-panel/help-panel.js'></script>
<script src='{{ STATIC_URL }}angular/wizard/wizard.js'></script>
<script src='{{ STATIC_URL }}angular/table/table.js'></script>
<script src='{{ STATIC_URL }}angular/modal/modal.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>

View File

@ -29,6 +29,7 @@ class ServicesTests(test.JasmineTests):
'angular/help-panel/help-panel.js',
'angular/wizard/wizard.js',
'angular/table/table.js',
'angular/modal/modal.js',
]
specs = [
'horizon/tests/jasmine/utilsSpec.js',
@ -37,8 +38,9 @@ class ServicesTests(test.JasmineTests):
'angular/help-panel/help-panel.spec.js',
'angular/wizard/wizard.spec.js',
'angular/table/table.spec.js',
'angular/modal/simple-modal.spec.js',
]
externalTemplates = [
'angular/help-panel/help-panel.html',
'angular/wizard/wizard.html',
'angular/wizard/wizard.html'
]

View File

@ -6,7 +6,7 @@
<div class="switcher_bar">
<div class="dropdown context-selection">
<button class="btn btn-default btn-topnav dropdown-toggle" data-toggle="dropdown">
<button class="btn btn-default btn-topnav dropdown-toggle">
{% show_overview %}
</button>
<div class="dropdown-menu topbar-dropdown-menu">
@ -27,7 +27,7 @@
<div id="user_info" class="pull-right">
<div id="profile_editor_switcher" class="dropdown switcher_bar" tabindex='1'>
<button class="btn btn-default btn-topnav dropdown-toggle" data-toggle="dropdown">
<button class="btn btn-default btn-topnav dropdown-toggle">
<span class="fa fa-user"></span>
{{ request.user.username }}
<span class="caret"></span>