webui: patternFly dialog

Reviewed-By: Endi Sukma Dewata <edewata@redhat.com>
This commit is contained in:
Petr Vobornik 2014-04-16 12:04:59 +02:00
parent 2f3dc7908d
commit faf4fea30f
7 changed files with 68 additions and 143 deletions

View File

@ -1,104 +0,0 @@
/**
* Authors:
* UXD team
* Petr Vobornik <pvoborni@redhat.com>
*
* Copyright (C) 2013 Red Hat
* see file 'COPYING' for use and warranty information
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
.rcue-dialog-background {
z-index: 1049;
background-color: rgba(0,0,0, 0.39);
width: 100%;
height: 100%;
position: fixed;
top: 0;
left: 0;
overflow: auto;
.rcue-dialog-container {
padding: 30px;
}
.rcue-dialog {
position: relative;
padding: 16px 22px;
max-width: 600px;
border: 1px solid #6e6d6d;
box-shadow: rgba(0,0,0, 0.39) 0 0 2px;
background-color: #fff;
margin: auto;
header {
margin-bottom: 48px;
h1 {
float: left;
margin: 0;
text-transform: none;
}
a {
float: right;
}
.rcue-button-close {
position: absolute;
top: 15px;
right: 19px;
display: inline-block;
.fa;
&:before {
content: @fa-var-times;
}
&:hover {
text-decoration: none;
}
}
}
.rcue-dialog-body {
position: relative;
top: 0;
bottom: 60px;
clear: both;
}
footer {
clear: both;
padding-left: 22px;
min-height: 25px;
button {
float: right;
margin: 0px 5px 5px;
}
.clear {
clear: both;
}
}
code {
white-space: normal;
}
}
}

View File

@ -6,7 +6,6 @@
@import "variables";
@import "mixins";
@import "dialog";
@import "brand";
@import "forms-override";
@import "alerts";

View File

@ -243,37 +243,39 @@ IPA.dialog = function(spec) {
}
that.dom_node = $('<div/>', {
'class': 'rcue-dialog-background',
keydown: that.on_key_down
'class': 'modal fade',
keydown: that.on_key_down,
tabindex: '-1',
'role': 'dialog',
'aria-labelledby': 'myLargeModalLabel',
'aria-hidden': 'true'
});
var container_node = $('<div/>', {
'class': 'rcue-dialog-container'
}).appendTo(that.dom_node);
that.dialog_node = $('<div/>', {
'class': 'rcue-dialog row',
'class': 'modal-dialog',
id: that.get_id(),
'data-name' : that.name,
role: 'dialog',
tabIndex: -1 // make the div focusable
}).appendTo(container_node);
}).appendTo(that.dom_node);
that.header_node = $('<header/>');
that.content_node = $('<div/>', { 'class': 'modal-content' }).
appendTo(that.dialog_node);
that.header_node = $('<div/>', { 'class': 'modal-header' });
that.create_header();
that.header_node.appendTo(that.dialog_node);
that.header_node.appendTo(that.content_node);
that.body_node = $('<div/>', {
'class': 'rcue-dialog-body row'
'class': 'modal-body'
});
// for backwards compatibility
that.container = that.body_node;
that.create_content();
that.body_node.appendTo(that.dialog_node);
that.body_node.appendTo(that.content_node);
that.footer_node = $('<footer/>');
that.footer_node = $('<div/>', { 'class': 'modal-footer' });
that.create_footer();
that.footer_node.appendTo(that.dialog_node);
that.footer_node.appendTo(that.content_node);
that.policies.post_create();
return that.dom_node;
@ -287,17 +289,22 @@ IPA.dialog = function(spec) {
that.create_header = function() {
that.header_node.empty();
that.title_node = $('<h1/>', {
text: that.title
}).appendTo(that.header_node);
that.title_close_button = $('<a/>', {
href: '#',
'class': 'rcue-button-close',
that.title_close_button = $('<button/>', {
'class': 'close',
'aria-hidden': 'true',
click: function() {
that.close();
}
}).appendTo(that.header_node);
$('<span/>', { 'class': 'fa fa-times' }).appendTo(that.title_close_button);
that.title_node = $('<h4/>', {
'class': 'modal-title',
text: that.title
}).appendTo(that.header_node);
return that.header_node;
};
@ -441,7 +448,15 @@ IPA.dialog = function(spec) {
that.register_listeners();
IPA.opened_dialogs.add_dialog(that);
this.dom_node.one('shown.bs.modal', function() {
that.focus_first_element();
});
this.dom_node.modal({
backdrop: 'static',
keyboard: 'false'
});
};
/**
@ -508,11 +523,18 @@ IPA.dialog = function(spec) {
that.remove_listeners();
that.dom_node.remove();
that.dom_node = null;
if (!that.dom_node) return;
var dom_node = that.dom_node;
that.dom_node.one('hidden.bs.modal', function() {
dom_node.remove();
that.dom_node = null;
IPA.opened_dialogs.remove_dialog(that);
IPA.opened_dialogs.focus_top();
});
that.dom_node.modal('hide');
};
/**

View File

@ -6,6 +6,7 @@
<script type="text/javascript" src="qunit.js"></script>
<script type="text/javascript" src="../js/libs/jquery.js"></script>
<script type="text/javascript" src="../js/libs/jquery.ordered-map.js"></script>
<script type="text/javascript" src="../js/libs/bootstrap.js"></script>
<script type="text/javascript" src="config.js"></script>
<script type="text/javascript" src="../js/dojo/dojo.js"></script>

View File

@ -6,6 +6,7 @@
<script type="text/javascript" src="qunit.js"></script>
<script type="text/javascript" src="../js/libs/jquery.js"></script>
<script type="text/javascript" src="../js/libs/jquery.ordered-map.js"></script>
<script type="text/javascript" src="../js/libs/bootstrap.js"></script>
<script type="text/javascript" src="config.js"></script>
<script type="text/javascript" src="../js/dojo/dojo.js"></script>

View File

@ -24,7 +24,8 @@ define([
'freeipa/rpc',
'freeipa/dialog',
'freeipa/widget',
'freeipa/details'],
'freeipa/details',
'freeipa/entity'],
function(IPA, $, rpc) {
return function() {
@ -157,7 +158,7 @@ test("Testing successful rpc.command().", function() {
ajax_counter, 1,
"Checking ajax invocation counter");
var dialog = $('#error_dialog');
var dialog = $('[data-name=error_dialog]');
ok(
dialog.length === 0,
@ -183,6 +184,7 @@ test("Testing unsuccessful rpc.command().", function() {
var success_handler_counter = 0;
var error_handler_counter = 0;
var dialog_selector = '[data-name=error_dialog]';
function success_handler(data, status, xhr) {
success_handler_counter++;
@ -225,12 +227,12 @@ test("Testing unsuccessful rpc.command().", function() {
}).execute();
function click_button(name) {
var dialog = $('#error_dialog');
var dialog = $(dialog_selector);
var btn = $('button[name='+name+']', dialog).first();
btn.trigger('click');
}
var dialog = $('#error_dialog');
var dialog = $(dialog_selector);
equals(
ajax_counter, 1,
@ -267,14 +269,15 @@ test("Testing unsuccessful rpc.command().", function() {
equals(ajax_counter, 3,
"Checking ajax invocation counter");
dialog = $('#error_dialog');
ok(dialog.length === 0,
"After cancel, the dialog box is closed.");
ok(success_handler_counter === 0 && error_handler_counter === 1,
"Only the error handler is called.");
// cleanup - qunit doesn't really play well with asynchronous opening and
// closing of dialogs
// opening and closing may be rewritten as asynchronous test
$('.modal').remove();
$('.modal-backdrop').remove();
$.ajax = orig;
});

View File

@ -504,9 +504,9 @@ class UI_driver(object):
"""
Get all dialogs in DOM
"""
s = 'div[role=dialog]'
s = '.modal-dialog'
if name:
s += " div[data-name='%s'" % name
s += "[data-name='%s']" % name
dialogs = self.find(s, By.CSS_SELECTOR, many=True)
if strict:
assert dialogs, "No dialogs found"
@ -526,7 +526,7 @@ class UI_driver(object):
"""
Get last opened error dialog or None.
"""
s = "div[role=dialog] div[data-name='%s']" % dialog_name
s = ".modal-dialog[data-name='%s']" % dialog_name
dialogs = self.find(s, By.CSS_SELECTOR, many=True)
dialog = None
if dialogs:
@ -542,7 +542,7 @@ class UI_driver(object):
info = None
if dialog:
body = self.find('.rcue-dialog-body', By.CSS_SELECTOR, dialog, strict=True)
body = self.find('.modal-body', By.CSS_SELECTOR, dialog, strict=True)
info = {
'name': dialog.get_attribute('data-name'),
'text': body.text,
@ -644,6 +644,9 @@ class UI_driver(object):
s = "[name=profile-menu] a[href='#%s']" % name
btn = self.find(s, By.CSS_SELECTOR, strict=True)
btn.click()
# action is usually followed by opening a dialog, add wait to compensate
# possible dialog transition effect
self.wait(0.5)
def get_form(self):
"""