mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
webui: activity indicators
https://fedorahosted.org/freeipa/ticket/4177 https://fedorahosted.org/freeipa/ticket/4255 Reviewed-By: Endi Sukma Dewata <edewata@redhat.com>
This commit is contained in:
@@ -728,7 +728,6 @@ fi
|
||||
%dir %{_usr}/share/ipa/ui/images
|
||||
%{_usr}/share/ipa/ui/images/*.jpg
|
||||
%{_usr}/share/ipa/ui/images/*.png
|
||||
%{_usr}/share/ipa/ui/images/*.gif
|
||||
%dir %{_usr}/share/ipa/wsgi
|
||||
%{_usr}/share/ipa/wsgi/plugins.py*
|
||||
%dir %{_sysconfdir}/ipa
|
||||
|
||||
@@ -10,8 +10,6 @@ app_DATA = \
|
||||
login-screen-background.jpg \
|
||||
login-screen-logo.png \
|
||||
product-name.png \
|
||||
spinner-header-1.gif \
|
||||
spinner-small.gif \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_DIST = \
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 9.2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.4 KiB |
@@ -24,14 +24,6 @@ textarea[readonly] {
|
||||
color: Gray;
|
||||
}
|
||||
|
||||
.network-activity-indicator {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
line-height: 16px;
|
||||
margin-right: 5px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* ---- Container ---- */
|
||||
|
||||
.app-container {
|
||||
|
||||
@@ -1,31 +1,74 @@
|
||||
|
||||
|
||||
#simple-container {
|
||||
|
||||
.global-activity-indicator {
|
||||
|
||||
bottom: initial;
|
||||
height: auto;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
color: white;
|
||||
width: 200px;
|
||||
text-align: left;
|
||||
|
||||
.activity-row {
|
||||
background-color: transparent;
|
||||
display: block;
|
||||
padding: 10px 20px;
|
||||
}
|
||||
|
||||
.activity-text {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.activity-text {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.slider {
|
||||
transition-property: all;
|
||||
transition-duration: .5s;
|
||||
transition-timing-function: cubic-bezier(0, 1, 0.5, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.global-activity-indicator {
|
||||
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
bottom: 0;
|
||||
text-shadow: none;
|
||||
color: white;
|
||||
color: black;
|
||||
font-size: 20px;
|
||||
font-weight: 300;
|
||||
width: 200px;
|
||||
padding: 15px 20px;
|
||||
height: 80px;
|
||||
margin: auto;
|
||||
text-align: center;
|
||||
|
||||
.activity-row {
|
||||
display: inline-block;
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
padding: 7px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.activity-text {
|
||||
padding: 3px 14px;
|
||||
background-color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.slider{
|
||||
.slider {
|
||||
overflow-y: hidden;
|
||||
transition-property: all;
|
||||
transition-duration: .5s;
|
||||
transition-timing-function: cubic-bezier(0, 1, 0.5, 1);
|
||||
}
|
||||
|
||||
#simple-container .slider.closed,
|
||||
.slider.closed {
|
||||
max-height: 0;
|
||||
padding: 0 20px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.validation-summary {
|
||||
|
||||
@@ -24,8 +24,9 @@ define([
|
||||
'dojo/when',
|
||||
'./plugin_loader',
|
||||
'./phases',
|
||||
'./Application_controller'
|
||||
],function(lang, Deferred, when, plugin_loader, phases, Application_controller) {
|
||||
'./Application_controller',
|
||||
'exports'
|
||||
],function(lang, Deferred, when, plugin_loader, phases, Application_controller, app) {
|
||||
|
||||
/**
|
||||
* Application wrapper
|
||||
@@ -35,7 +36,7 @@ define([
|
||||
* @class app
|
||||
* @singleton
|
||||
*/
|
||||
var app = {
|
||||
lang.mixin(app, {
|
||||
|
||||
/**
|
||||
* Application instance
|
||||
@@ -89,7 +90,7 @@ define([
|
||||
phases.controller.run();
|
||||
}));
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
return app;
|
||||
});
|
||||
@@ -277,6 +277,14 @@ IPA.dialog = function(spec) {
|
||||
that.create_footer();
|
||||
that.footer_node.appendTo(that.content_node);
|
||||
|
||||
that.activity_indicator = IPA.activity_widget({
|
||||
text: text.get('@i18n:status.working', 'Working'),
|
||||
mode: 'icon',
|
||||
visible: false
|
||||
});
|
||||
that.activity_indicator_node = $('<div/>').appendTo(that.dom_node);
|
||||
that.activity_indicator.create(that.activity_indicator_node);
|
||||
|
||||
that.policies.post_create();
|
||||
return that.dom_node;
|
||||
};
|
||||
|
||||
@@ -318,8 +318,6 @@ IPA.hbac.test_select_facet = function(spec) {
|
||||
}
|
||||
}).appendTo(filter_container);
|
||||
|
||||
header.append(IPA.create_network_spinner());
|
||||
|
||||
var content = $('<div/>', {
|
||||
'class': 'hbac-test-content'
|
||||
}).appendTo(container);
|
||||
|
||||
@@ -318,7 +318,6 @@ var IPA = function () {
|
||||
*/
|
||||
that.display_activity_icon = function() {
|
||||
that.network_call_count++;
|
||||
$('.network-activity-indicator').css('display', '');
|
||||
if (that.network_call_count === 1) {
|
||||
topic.publish('network-activity-start');
|
||||
}
|
||||
@@ -333,7 +332,6 @@ var IPA = function () {
|
||||
that.network_call_count--;
|
||||
|
||||
if (0 === that.network_call_count) {
|
||||
$('.network-activity-indicator').css('display', 'none');
|
||||
topic.publish('network-activity-end');
|
||||
}
|
||||
};
|
||||
@@ -724,21 +722,6 @@ IPA.get_member_attribute = function(obj_name, member) {
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create HTML representation of network spinner.
|
||||
* @member IPA
|
||||
* @return {HTMLElement} Network spinner node
|
||||
*/
|
||||
IPA.create_network_spinner = function(){
|
||||
var span = $('<span/>', {
|
||||
'class': 'network-activity-indicator'
|
||||
});
|
||||
$('<img/>', {
|
||||
src: 'images/spinner-small.gif'
|
||||
}).appendTo(span);
|
||||
return span;
|
||||
};
|
||||
|
||||
/**
|
||||
* Dirty dialog
|
||||
*
|
||||
|
||||
@@ -109,8 +109,6 @@ IPA.search_facet = function(spec, no_init) {
|
||||
'class': 'right-aligned-facet-controls'
|
||||
}).appendTo(that.controls);
|
||||
|
||||
div.append(IPA.create_network_spinner());
|
||||
|
||||
that.filter_container = $('<div/>', {
|
||||
'class': 'search-filter'
|
||||
}).appendTo(div);
|
||||
|
||||
@@ -164,6 +164,7 @@ IPA.widget = function(spec) {
|
||||
* @param {HTMLElement} container - Container node
|
||||
*/
|
||||
that.create = function(container) {
|
||||
container = $(container);
|
||||
container.addClass(that.base_css_class);
|
||||
container.addClass(that.css_class);
|
||||
that.container = container;
|
||||
@@ -5464,6 +5465,8 @@ exp.activity_widget = IPA.activity_widget = function(spec) {
|
||||
|
||||
that.text_node = null;
|
||||
|
||||
that.row_node = null;
|
||||
|
||||
that.dots = spec.dots || 0;
|
||||
|
||||
that.step = spec.step || 1;
|
||||
@@ -5474,16 +5477,41 @@ exp.activity_widget = IPA.activity_widget = function(spec) {
|
||||
|
||||
that.speed = spec.speed || 800;
|
||||
|
||||
that.icon = spec.icon || 'fa fa-spinner fa-spin';
|
||||
|
||||
/**
|
||||
* Operation mode
|
||||
*
|
||||
* ['dots', 'icon']
|
||||
*
|
||||
* @property {string}
|
||||
*/
|
||||
that.mode = spec.mode || "dots";
|
||||
|
||||
that.activate_event = spec.activate_event || 'network-activity-start';
|
||||
that.deactivate_event = spec.deactivate_event || 'network-activity-end';
|
||||
|
||||
that.create = function(container) {
|
||||
that.widget_create(container);
|
||||
that.add_class('global-activity-indicator slider closed');
|
||||
that.row_node = $("<div/>", { 'class': 'activity-row' }).appendTo(that.container);
|
||||
that.text_node = $("<div/>", {
|
||||
text: that.text
|
||||
}).appendTo(that.container);
|
||||
if (that.visible) that.start();
|
||||
text: that.text,
|
||||
'class': 'activity-text'
|
||||
}).appendTo(that.row_node);
|
||||
|
||||
if (that.mode === 'icon') {
|
||||
that.text_node.prepend(' ');
|
||||
$('<i/>', {
|
||||
'class': that.icon
|
||||
}).prependTo(that.text_node);
|
||||
}
|
||||
|
||||
if (that.visible) {
|
||||
that.show();
|
||||
} else {
|
||||
that.hide();
|
||||
}
|
||||
that.set_visible(that.visible);
|
||||
topic.subscribe(that.activate_event, function() {
|
||||
that.show();
|
||||
@@ -5493,25 +5521,29 @@ exp.activity_widget = IPA.activity_widget = function(spec) {
|
||||
});
|
||||
};
|
||||
|
||||
that.start = function() {
|
||||
that.toggle_timer = function(start) {
|
||||
|
||||
that.timer = window.setInterval( function() {
|
||||
that.make_step();
|
||||
}, that.speed);
|
||||
};
|
||||
if (that.mode === 'icon') return;
|
||||
|
||||
that.stop = function() {
|
||||
if (that.timer) window.clearInterval(that.timer);
|
||||
if (start) {
|
||||
that.timer = window.setInterval( function() {
|
||||
that.make_step();
|
||||
}, that.speed);
|
||||
} else {
|
||||
if (that.timer) window.clearInterval(that.timer);
|
||||
}
|
||||
};
|
||||
|
||||
that.hide = function() {
|
||||
that.toggle_class('closed', true);
|
||||
that.stop();
|
||||
that.row_node.detach(); // to save CPU time (spinner icon)
|
||||
that.toggle_timer(false);
|
||||
};
|
||||
|
||||
that.show = function() {
|
||||
that.toggle_class('closed', false);
|
||||
that.start();
|
||||
that.row_node.appendTo(that.container);
|
||||
that.toggle_timer(true);
|
||||
};
|
||||
|
||||
that.make_step = function() {
|
||||
|
||||
@@ -31,11 +31,13 @@ define(['dojo/_base/declare',
|
||||
'./Menu',
|
||||
'./DropdownWidget',
|
||||
'./FacetContainer',
|
||||
'../text',
|
||||
'../widget',
|
||||
'dojo/NodeList-dom'
|
||||
],
|
||||
function(declare, lang, array, dom, construct, prop, dom_class,
|
||||
dom_style, query, on, Menu, DropdownWidget,
|
||||
FacetContainer) {
|
||||
FacetContainer, text, widgets) {
|
||||
|
||||
/**
|
||||
* Main application widget
|
||||
@@ -63,10 +65,14 @@ define(['dojo/_base/declare',
|
||||
|
||||
menu_node: null,
|
||||
|
||||
indicator_node: null,
|
||||
|
||||
id: 'container',
|
||||
|
||||
logged: false,
|
||||
|
||||
use_activity_indicator: true,
|
||||
|
||||
_loggedSetter: function(value) {
|
||||
this.logged = value;
|
||||
//TODO show/hide menu
|
||||
@@ -97,6 +103,11 @@ define(['dojo/_base/declare',
|
||||
this.content_node = construct.create('div', {
|
||||
'class': 'content'
|
||||
}, this.dom_node);
|
||||
|
||||
if (this.use_activity_indicator) {
|
||||
this.indicator_node = construct.create('div', {}, this.dom_node);
|
||||
this.activity_indicator.create(this.indicator_node);
|
||||
}
|
||||
},
|
||||
|
||||
_render_navigation: function() {
|
||||
@@ -162,14 +173,6 @@ define(['dojo/_base/declare',
|
||||
'class': 'header-passwordexpires'
|
||||
}, this.nav_util_tool_node);
|
||||
|
||||
var network_activity = construct.create('li', {
|
||||
'class': 'header-network-activity-indicator network-activity-indicator'
|
||||
}, this.nav_util_tool_node);
|
||||
|
||||
construct.create('img', {
|
||||
src: 'images/spinner-header-1.gif'
|
||||
}, network_activity);
|
||||
|
||||
var user_toggle = this._render_user_toggle_nodes();
|
||||
this.user_menu.set('toggle_content', user_toggle);
|
||||
construct.place(this.user_menu.render(), this.nav_util_tool_node);
|
||||
@@ -241,6 +244,10 @@ define(['dojo/_base/declare',
|
||||
constructor: function(spec) {
|
||||
spec = spec || {};
|
||||
this.menu_widget = new Menu();
|
||||
this.activity_indicator = widgets.activity_widget({
|
||||
mode: 'icon',
|
||||
text: text.get('@i18n:status.working', 'Working')
|
||||
});
|
||||
this.user_menu = new DropdownWidget({
|
||||
el_type: 'li',
|
||||
name: 'profile-menu',
|
||||
|
||||
@@ -534,7 +534,8 @@
|
||||
"disabled": "Disabled",
|
||||
"enable": "Enable",
|
||||
"enabled": "Enabled",
|
||||
"label": "Status"
|
||||
"label": "Status",
|
||||
"working": "Working"
|
||||
},
|
||||
"tabs": {
|
||||
"audit": "Audit",
|
||||
|
||||
@@ -671,6 +671,7 @@ class i18n_messages(Command):
|
||||
"enable": _("Enable"),
|
||||
"enabled": _("Enabled"),
|
||||
"label": _("Status"),
|
||||
"working": _("Working"),
|
||||
},
|
||||
"tabs": {
|
||||
"audit": _("Audit"),
|
||||
|
||||
@@ -35,6 +35,7 @@ try:
|
||||
from selenium import webdriver
|
||||
from selenium.common.exceptions import NoSuchElementException
|
||||
from selenium.common.exceptions import InvalidElementStateException
|
||||
from selenium.common.exceptions import StaleElementReferenceException
|
||||
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
from selenium.webdriver.common.by import By
|
||||
@@ -262,7 +263,7 @@ class UI_driver(object):
|
||||
"""
|
||||
Test if dependencies were loaded. (Checks if UI has been rendered)
|
||||
"""
|
||||
indicator = self.find(".network-activity-indicator", By.CSS_SELECTOR)
|
||||
indicator = self.find(".global-activity-indicator", By.CSS_SELECTOR)
|
||||
return indicator is not None
|
||||
|
||||
def has_ca(self):
|
||||
@@ -287,11 +288,15 @@ class UI_driver(object):
|
||||
"""
|
||||
Check if there is running AJAX request
|
||||
"""
|
||||
indicator = self.find(".network-activity-indicator", By.CSS_SELECTOR)
|
||||
i_visible = indicator and indicator.is_displayed()
|
||||
global_indicator = self.find(".global-activity-indicator", By.CSS_SELECTOR)
|
||||
g_visible = global_indicator and global_indicator.is_displayed()
|
||||
return i_visible or g_visible
|
||||
global_indicators = self.find(".global-activity-indicator", By.CSS_SELECTOR, many=True)
|
||||
for el in global_indicators:
|
||||
try:
|
||||
if not self.has_class(el, 'closed'):
|
||||
return True
|
||||
except StaleElementReferenceException:
|
||||
# we don't care. Happens when indicator is part of removed dialog.
|
||||
continue
|
||||
return False
|
||||
|
||||
def wait(self, seconds=0.2):
|
||||
"""
|
||||
@@ -635,7 +640,7 @@ class UI_driver(object):
|
||||
btn.click()
|
||||
self.wait_for_request()
|
||||
|
||||
def profile_menu_action (self, name):
|
||||
def profile_menu_action(self, name):
|
||||
"""
|
||||
Execute action from profile menu
|
||||
"""
|
||||
@@ -1480,6 +1485,12 @@ class UI_driver(object):
|
||||
# add multiple at once and test table delete button
|
||||
self.add_table_associations(table, keys, delete=True)
|
||||
|
||||
def has_class(self, el, cls):
|
||||
"""
|
||||
Check if el has CSS class
|
||||
"""
|
||||
return cls in el.get_attribute("class").split()
|
||||
|
||||
def skip(self, reason):
|
||||
"""
|
||||
Skip tests
|
||||
@@ -1543,8 +1554,7 @@ class UI_driver(object):
|
||||
facet = self.get_facet()
|
||||
btn = self.find(s, By.CSS_SELECTOR, facet, strict=True)
|
||||
cls = 'action-button-disabled'
|
||||
has_cls = cls in btn.get_attribute("class").split()
|
||||
valid = enabled ^ has_cls
|
||||
valid = enabled ^ self.has_class(btn, cls)
|
||||
assert btn.is_displayed(), 'Button is not displayed'
|
||||
assert valid, 'Button has incorrect enabled state.'
|
||||
|
||||
@@ -1648,7 +1658,7 @@ class UI_driver(object):
|
||||
"""
|
||||
Assert that element has certain class
|
||||
"""
|
||||
valid = cls in element.get_attribute('class').split()
|
||||
valid = self.has_class(element, cls)
|
||||
if negative:
|
||||
assert not valid, "Element contains unwanted class: %s" % cls
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user