diff options
-rw-r--r-- | freeipa.spec.in | 1 | ||||
-rw-r--r-- | install/ui/images/Makefile.am | 2 | ||||
-rw-r--r-- | install/ui/images/spinner-header-1.gif | bin | 9427 -> 0 bytes | |||
-rw-r--r-- | install/ui/images/spinner-small.gif | bin | 3532 -> 0 bytes | |||
-rw-r--r-- | install/ui/ipa.css | 8 | ||||
-rw-r--r-- | install/ui/less/widgets.less | 61 | ||||
-rw-r--r-- | install/ui/src/freeipa/app_container.js | 9 | ||||
-rw-r--r-- | install/ui/src/freeipa/dialog.js | 8 | ||||
-rw-r--r-- | install/ui/src/freeipa/hbactest.js | 2 | ||||
-rw-r--r-- | install/ui/src/freeipa/ipa.js | 17 | ||||
-rw-r--r-- | install/ui/src/freeipa/search.js | 2 | ||||
-rw-r--r-- | install/ui/src/freeipa/widget.js | 56 | ||||
-rw-r--r-- | install/ui/src/freeipa/widgets/App.js | 25 | ||||
-rw-r--r-- | install/ui/test/data/ipa_init.json | 3 | ||||
-rw-r--r-- | ipalib/plugins/internal.py | 1 | ||||
-rw-r--r-- | ipatests/test_webui/ui_driver.py | 30 |
16 files changed, 148 insertions, 77 deletions
diff --git a/freeipa.spec.in b/freeipa.spec.in index b8890ff4e..e2b9e5164 100644 --- a/freeipa.spec.in +++ b/freeipa.spec.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 diff --git a/install/ui/images/Makefile.am b/install/ui/images/Makefile.am index c17ef1505..e74d747b7 100644 --- a/install/ui/images/Makefile.am +++ b/install/ui/images/Makefile.am @@ -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 = \ diff --git a/install/ui/images/spinner-header-1.gif b/install/ui/images/spinner-header-1.gif Binary files differdeleted file mode 100644 index b3d5e213f..000000000 --- a/install/ui/images/spinner-header-1.gif +++ /dev/null diff --git a/install/ui/images/spinner-small.gif b/install/ui/images/spinner-small.gif Binary files differdeleted file mode 100644 index 1a2da81c2..000000000 --- a/install/ui/images/spinner-small.gif +++ /dev/null diff --git a/install/ui/ipa.css b/install/ui/ipa.css index 69855d7c0..ee3cbddc6 100644 --- a/install/ui/ipa.css +++ b/install/ui/ipa.css @@ -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 { diff --git a/install/ui/less/widgets.less b/install/ui/less/widgets.less index 543bafeed..3fc8cb5b7 100644 --- a/install/ui/less/widgets.less +++ b/install/ui/less/widgets.less @@ -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 { diff --git a/install/ui/src/freeipa/app_container.js b/install/ui/src/freeipa/app_container.js index c56fc3bfe..ec2d71e44 100644 --- a/install/ui/src/freeipa/app_container.js +++ b/install/ui/src/freeipa/app_container.js @@ -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; });
\ No newline at end of file diff --git a/install/ui/src/freeipa/dialog.js b/install/ui/src/freeipa/dialog.js index 459933336..578acb55d 100644 --- a/install/ui/src/freeipa/dialog.js +++ b/install/ui/src/freeipa/dialog.js @@ -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; }; diff --git a/install/ui/src/freeipa/hbactest.js b/install/ui/src/freeipa/hbactest.js index a936029b6..7a9d85ab3 100644 --- a/install/ui/src/freeipa/hbactest.js +++ b/install/ui/src/freeipa/hbactest.js @@ -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); diff --git a/install/ui/src/freeipa/ipa.js b/install/ui/src/freeipa/ipa.js index ad70f9f71..d74881924 100644 --- a/install/ui/src/freeipa/ipa.js +++ b/install/ui/src/freeipa/ipa.js @@ -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'); } }; @@ -725,21 +723,6 @@ IPA.get_member_attribute = function(obj_name, member) { }; /** - * 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 * * Should be used as an indication of unsaved changes on page when leaving the diff --git a/install/ui/src/freeipa/search.js b/install/ui/src/freeipa/search.js index f9739f4c2..9c0676313 100644 --- a/install/ui/src/freeipa/search.js +++ b/install/ui/src/freeipa/search.js @@ -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); diff --git a/install/ui/src/freeipa/widget.js b/install/ui/src/freeipa/widget.js index d4c9cb366..08270e319 100644 --- a/install/ui/src/freeipa/widget.js +++ b/install/ui/src/freeipa/widget.js @@ -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() { diff --git a/install/ui/src/freeipa/widgets/App.js b/install/ui/src/freeipa/widgets/App.js index c7924e8df..b70b14a94 100644 --- a/install/ui/src/freeipa/widgets/App.js +++ b/install/ui/src/freeipa/widgets/App.js @@ -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', diff --git a/install/ui/test/data/ipa_init.json b/install/ui/test/data/ipa_init.json index 3d143d85d..500aead41 100644 --- a/install/ui/test/data/ipa_init.json +++ b/install/ui/test/data/ipa_init.json @@ -534,7 +534,8 @@ "disabled": "Disabled", "enable": "Enable", "enabled": "Enabled", - "label": "Status" + "label": "Status", + "working": "Working" }, "tabs": { "audit": "Audit", diff --git a/ipalib/plugins/internal.py b/ipalib/plugins/internal.py index e6c17f273..0f7601955 100644 --- a/ipalib/plugins/internal.py +++ b/ipalib/plugins/internal.py @@ -671,6 +671,7 @@ class i18n_messages(Command): "enable": _("Enable"), "enabled": _("Enabled"), "label": _("Status"), + "working": _("Working"), }, "tabs": { "audit": _("Audit"), diff --git a/ipatests/test_webui/ui_driver.py b/ipatests/test_webui/ui_driver.py index 4b3069640..8a8293b9b 100644 --- a/ipatests/test_webui/ui_driver.py +++ b/ipatests/test_webui/ui_driver.py @@ -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: |