Repository: ambari Updated Branches: refs/heads/trunk 4d0a5cf65 -> a13e96e75
AMBARI-20510. Oozie Database URL property was changed by itself (alexantonenko) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/a13e96e7 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/a13e96e7 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/a13e96e7 Branch: refs/heads/trunk Commit: a13e96e755f5daf692375b15bd32767eda987b6d Parents: 4d0a5cf Author: Alex Antonenko <[email protected]> Authored: Mon Mar 20 22:40:53 2017 +0200 Committer: Alex Antonenko <[email protected]> Committed: Tue Mar 21 02:55:15 2017 +0200 ---------------------------------------------------------------------- ambari-web/app/assets/test/tests.js | 1 + .../controllers/main/service/info/configs.js | 17 +++ ambari-web/app/mixins.js | 1 + .../mixins/common/loading_overlay_support.js | 136 +++++++++++++++++++ ambari-web/app/styles/application.less | 11 ++ ambari-web/app/styles/config_history_flow.less | 4 +- .../configs/service_config_layout_tab.hbs | 1 + .../configs/config_category_container_view.js | 28 +++- .../configs/service_config_layout_tab_view.js | 9 +- .../common/loading_overlay_support_test.js | 96 +++++++++++++ 10 files changed, 297 insertions(+), 7 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/a13e96e7/ambari-web/app/assets/test/tests.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/assets/test/tests.js b/ambari-web/app/assets/test/tests.js index 8fadb44..d404d1f 100644 --- a/ambari-web/app/assets/test/tests.js +++ b/ambari-web/app/assets/test/tests.js @@ -179,6 +179,7 @@ var files = [ 'test/mixins/common/widgets/export_metrics_mixin_test', 'test/mixins/common/widgets/time_range_mixin_test', 'test/mixins/common/widgets/widget_section_test', + 'test/mixins/common/loading_overlay_support_test', 'test/mixins/common/kdc_credentials_controller_mixin_test', 'test/mixins/common/localStorage_test', 'test/mixins/common/reload_popup_test', http://git-wip-us.apache.org/repos/asf/ambari/blob/a13e96e7/ambari-web/app/controllers/main/service/info/configs.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/controllers/main/service/info/configs.js b/ambari-web/app/controllers/main/service/info/configs.js index 7eae5c3..8436086 100644 --- a/ambari-web/app/controllers/main/service/info/configs.js +++ b/ambari-web/app/controllers/main/service/info/configs.js @@ -46,6 +46,23 @@ App.MainServiceInfoConfigsController = Em.Controller.extend(App.AddSecurityConfi groupsStore: App.ServiceConfigGroup.find(), /** + * Configs tab for current service + * + * @type {App.Tab[]} + */ + activeServiceTabs: function () { + var selectedServiceName = this.get('selectedService.serviceName'); + return selectedServiceName ? App.Tab.find().filterProperty('serviceName', selectedServiceName) : []; + }.property('selectedService.serviceName'), + + /** + * Currently opened configs tab + * + * @type {App.Tab} + */ + activeTab: Em.computed.findBy('activeServiceTabs', 'isActive', true), + + /** * config groups for current service * @type {App.ConfigGroup[]} */ http://git-wip-us.apache.org/repos/asf/ambari/blob/a13e96e7/ambari-web/app/mixins.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/mixins.js b/ambari-web/app/mixins.js index 9d833b5..4fdfa54 100644 --- a/ambari-web/app/mixins.js +++ b/ambari-web/app/mixins.js @@ -30,6 +30,7 @@ require('mixins/common/serverValidator'); require('mixins/common/table_server_view_mixin'); require('mixins/common/table_server_mixin'); require('mixins/common/track_request_mixin'); +require('mixins/common/loading_overlay_support'); require('mixins/main/dashboard/widgets/editable'); require('mixins/main/dashboard/widgets/editable_with_limit'); require('mixins/main/dashboard/widgets/single_numeric_threshold'); http://git-wip-us.apache.org/repos/asf/ambari/blob/a13e96e7/ambari-web/app/mixins/common/loading_overlay_support.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/mixins/common/loading_overlay_support.js b/ambari-web/app/mixins/common/loading_overlay_support.js new file mode 100644 index 0000000..fd898bc --- /dev/null +++ b/ambari-web/app/mixins/common/loading_overlay_support.js @@ -0,0 +1,136 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var App = require('app'); + +/** + * This Mixin is used for views that should "wait" while some external action is executed (e.g., ajax-request). + * You should set <code>fieldToObserve</code> to fieldName which value determines begin and end of such action + * When <code>fieldToObserve</code>-value becomes truly, overlay is shown + * Overlay size is taken from current view size + * + * Usage: + * + * <pre> + * Em.View.extend(App.LoadingOverlaySupport, { + * fieldToObserve: 'someFieldName' + * }); + * </pre> + * + * Don't forget to add <code><div class="loading-overlay"></div></code> to your view's template + * + * @type {Em.Mixin} + */ +App.LoadingOverlaySupport = Em.Mixin.create({ + + /** + * @type {string} + */ + fieldToObserve: '', + + /** + * Determines if <code>handleFieldChanges</code> should be call on <code>didInsertElement</code> + * + * @type {boolean} + * @default {true} + */ + handleFieldChangesOnDidInsert: true, + + init() { + const fieldToObserve = this.get('fieldToObserve'); + Em.assert('`fieldToObserve` should be defined', fieldToObserve); + this.addObserver(fieldToObserve, this, 'handleFieldChanges'); + return this._super(...arguments); + }, + + /** + * Don't allow change `fieldToObserve` after view is initialized. This may cause a lot of errors + */ + doNotChangeFieldToObserve: function () { + Em.assert('Do not change `fieldToObserve` after view is initialized', false); + }.observes('fieldToObserve'), + + /** + * Value in the <code>fieldToObserve</code> may be <code>truly</code> on view init, so + * <code>handleFieldChanges</code> should be called to check this + * + * Example use case: + * Switching tabs on the configs page for some service. Every tab should be disabled when switching on it + * if needed property is set to <code>true</code> + * + * @returns {*} + */ + didInsertElement() { + this._super(...arguments); + if (this.get('handleFieldChangesOnDidInsert')) { + this.handleFieldChanges(); + } + }, + + /** + * Remove observer `handleFieldChanges` + */ + willDestroyElement() { + const fieldToObserve = this.get('fieldToObserve'); + this.removeObserver(fieldToObserve, this, 'handleFieldChanges'); + this._cleanUp(); + }, + + /** + * Show overlay when `fieldToObserve`-value is set to some truly + * Remove overlay otherwise + */ + handleFieldChanges() { + const fieldToObserve = this.get('fieldToObserve'); + const fieldValue = this.get(fieldToObserve); + const overlay = this.$('.loading-overlay'); + let polling = this.get('_polling'); + if (fieldValue) { + if (!polling) { + polling = setInterval(() => { + if (fieldValue && overlay) { + overlay.addClass('overlay-visible').css({ + width: this.$().width(), + height: this.$().height() + }); + } + else { + this._cleanUp(); + } + }, 50); + this.set('_polling', polling); + } + } + else { + this._cleanUp(); + } + }, + + /** + * @private + */ + _cleanUp() { + clearInterval(this.get('_polling')); + this.set('_polling', null); + const overlay = this.$('.loading-overlay'); + if (overlay) { + overlay.removeClass('overlay-visible'); + } + } + +}); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/a13e96e7/ambari-web/app/styles/application.less ---------------------------------------------------------------------- diff --git a/ambari-web/app/styles/application.less b/ambari-web/app/styles/application.less index 7140ad7..0df1644 100644 --- a/ambari-web/app/styles/application.less +++ b/ambari-web/app/styles/application.less @@ -2660,3 +2660,14 @@ a.abort-icon:hover { padding: 4px; background-color: rgba(0, 0, 0, 0.6); } + +.loading-overlay { + background: #fff; + opacity: 0.3; + display: none; + position: absolute; + z-index: 10; + &.overlay-visible { + display: block; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/a13e96e7/ambari-web/app/styles/config_history_flow.less ---------------------------------------------------------------------- diff --git a/ambari-web/app/styles/config_history_flow.less b/ambari-web/app/styles/config_history_flow.less index 41d23d4..d47a28c 100644 --- a/ambari-web/app/styles/config_history_flow.less +++ b/ambari-web/app/styles/config_history_flow.less @@ -41,7 +41,7 @@ } .dependencies-info-bar-wrapper { - z-index: 3; + z-index: 100; margin: 0 0 20px; .alert { margin: 0; @@ -191,7 +191,7 @@ } .version-info-bar-wrapper { margin: 0; - z-index: 3; + z-index: 100; } .version-info-bar { http://git-wip-us.apache.org/repos/asf/ambari/blob/a13e96e7/ambari-web/app/templates/common/configs/service_config_layout_tab.hbs ---------------------------------------------------------------------- diff --git a/ambari-web/app/templates/common/configs/service_config_layout_tab.hbs b/ambari-web/app/templates/common/configs/service_config_layout_tab.hbs index f78952c..3709033 100644 --- a/ambari-web/app/templates/common/configs/service_config_layout_tab.hbs +++ b/ambari-web/app/templates/common/configs/service_config_layout_tab.hbs @@ -19,6 +19,7 @@ {{#if view.dataIsReady}} {{#unless tab.isHiddenByFilter}} <div class="section-layout-container"> + <div class="loading-overlay"></div> <table class="config-section-table"> {{#each row in tab.sectionRows}} <tr> http://git-wip-us.apache.org/repos/asf/ambari/blob/a13e96e7/ambari-web/app/views/common/configs/config_category_container_view.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/views/common/configs/config_category_container_view.js b/ambari-web/app/views/common/configs/config_category_container_view.js index 37fc10f..882b0f0 100644 --- a/ambari-web/app/views/common/configs/config_category_container_view.js +++ b/ambari-web/app/views/common/configs/config_category_container_view.js @@ -20,7 +20,7 @@ var App = require('app'); var lazyLoading = require('utils/lazy_loading'); -App.ConfigCategoryContainerView = Em.ContainerView.extend({ +App.ConfigCategoryContainerView = Em.ContainerView.extend(App.LoadingOverlaySupport, { lazyLoading: null, @@ -28,6 +28,10 @@ App.ConfigCategoryContainerView = Em.ContainerView.extend({ classNames: ['accordion'], + fieldToObserve: 'controller.recommendationsInProgress', + + handleFieldChangesOnDidInsert: false, + didInsertElement: function () { this.pushViews(); this._super(); @@ -35,16 +39,32 @@ App.ConfigCategoryContainerView = Em.ContainerView.extend({ willDestroyElement: function () { if (this.get('lazyLoading')) { - lazyLoading.terminate(this.get('lazyLoading')) + lazyLoading.terminate(this.get('lazyLoading')); } this._super(); }, + /** + * extra calls for <code>handleFieldChanges</code>: + * <ul> + * <li>child views are pushed to this (lazy loading is used)</li> + * <li>tab is switched</li> + * </ul> + */ + checkOverlay: function () { + this.handleFieldChanges(); + }.observes('isLoaded', 'controller.activeTab.id', 'controller.activeTab.isRendered'), + pushViews: function () { var self = this; - var categoriesViews = []; + // Create view with loading-overlay. For some unknown reasons overlay can't be added to the container view + var categoriesViews = [ + Em.View.extend({ + template: Em.Handlebars.compile('<div class="loading-overlay"></div>') + }).create() + ]; var categories = this.get('categories'); - if (!categories) return false; + if (!categories) return; categories.forEach(function (category) { var viewClass = category.isCustomView ? category.customView : App.ServiceConfigsByCategoryView; categoriesViews.push(viewClass.create({ http://git-wip-us.apache.org/repos/asf/ambari/blob/a13e96e7/ambari-web/app/views/common/configs/service_config_layout_tab_view.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/views/common/configs/service_config_layout_tab_view.js b/ambari-web/app/views/common/configs/service_config_layout_tab_view.js index ccbb148..fad73e1 100644 --- a/ambari-web/app/views/common/configs/service_config_layout_tab_view.js +++ b/ambari-web/app/views/common/configs/service_config_layout_tab_view.js @@ -18,7 +18,7 @@ var App = require('app'); -App.ServiceConfigLayoutTabView = Em.View.extend(App.ConfigOverridable, { +App.ServiceConfigLayoutTabView = Em.View.extend(App.ConfigOverridable, App.LoadingOverlaySupport, { /** * Determines if view is editable @@ -43,6 +43,8 @@ App.ServiceConfigLayoutTabView = Em.View.extend(App.ConfigOverridable, { templateName: require('templates/common/configs/service_config_layout_tab'), + fieldToObserve: 'controller.recommendationsInProgress', + classNames: ['enhanced-config-tab-content'], /** * ConfigType-Widget map @@ -71,6 +73,10 @@ App.ServiceConfigLayoutTabView = Em.View.extend(App.ConfigOverridable, { num_llap_nodes: App.NumLlapNodesWidgetMixin }, + checkOverlay: function () { + this.handleFieldChanges(); + }.observes('controller.activeTab.id', 'controller.activeTab.isRendered'), + /** * Prepare configs for render * <code>subsection.configs</code> is an array of App.StackConfigProperty, but not App.ConfigProperty, @@ -203,6 +209,7 @@ App.ServiceConfigLayoutTabView = Em.View.extend(App.ConfigOverridable, { } this.set('content.isConfigsPrepared', true); this.set('dataIsReady', true); + this._super(...arguments); } }); http://git-wip-us.apache.org/repos/asf/ambari/blob/a13e96e7/ambari-web/test/mixins/common/loading_overlay_support_test.js ---------------------------------------------------------------------- diff --git a/ambari-web/test/mixins/common/loading_overlay_support_test.js b/ambari-web/test/mixins/common/loading_overlay_support_test.js new file mode 100644 index 0000000..cf53d56 --- /dev/null +++ b/ambari-web/test/mixins/common/loading_overlay_support_test.js @@ -0,0 +1,96 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var App = require('app'); + +var view; + +describe('App.LoadingOverlaySupport', function () { + + beforeEach(function (done) { + view = Em.View.extend(App.LoadingOverlaySupport, { + flag: false, + fieldToObserve: 'flag', + attributeBindings: ['style'], + style: 'height: 100px; width: 200px;', + template: Em.Handlebars.compile('<div class="loading-overlay"></div>'), + didInsertElement: function () { + this._super(); + done(); + } + }).create(); + view.appendTo('#wrapper'); + sinon.stub(window, 'setInterval', Em.clb); + }); + + afterEach(function () { + view.destroy(); + window.setInterval.restore(); + }); + + describe('#doNotChangeFieldToObserve', function () { + + it('should throw an error', function () { + expect(function () { + view.set('fieldToObserve', 'somethingNew'); + }).to.throw(Error, 'Do not change `fieldToObserve` after view is initialized'); + }); + + }); + + describe('#handleFieldChanges', function () { + + describe('overlay is shown', function () { + + beforeEach(function () { + view.set('flag', true); + }); + + afterEach(function () { + view.set('flag', false); + }); + + it('should add overlay', function () { + expect(view.$('.loading-overlay.overlay-visible').length).to.be.equal(1); + }); + + it('overlay width is correct', function () { + expect(view.$('.loading-overlay.overlay-visible').width()).to.be.equal(200); + }); + + it('overlay height is correct', function () { + expect(view.$('.loading-overlay.overlay-visible').height()).to.be.equal(100); + }); + + }); + + describe('overlay is hidden', function () { + + it('overlay is depends of `flag`-value', function () { + expect(view.$('.loading-overlay.overlay-visible').length).to.be.equal(0); + view.set('flag', true); + expect(view.$('.loading-overlay.overlay-visible').length).to.be.equal(1); + view.set('flag', false); + expect(view.$('.loading-overlay.overlay-visible').length).to.be.equal(0); + }); + + }); + + }); + +});
