From bae2ff8761b0fcb5a1d6a5dbe7fe4452d337232c Mon Sep 17 00:00:00 2001 From: webchick <drupal@webchick.net> Date: Fri, 15 Jun 2018 09:11:46 -0700 Subject: [PATCH] Issue #2407859 by vaplas, lauriii, vprocessor, drpal, jwilson3, sidharthap, aprogs, finnsky: Allow theming throbber element --- core/misc/ajax.es6.js | 41 ++++++- core/misc/ajax.js | 22 +++- core/modules/field_ui/field_ui.es6.js | 2 +- core/modules/field_ui/field_ui.js | 2 +- .../modules/hold_test/hold_test.info.yml | 6 + .../tests/modules/hold_test/hold_test.install | 14 +++ .../tests/modules/hold_test/hold_test.module | 26 ++++ .../modules/hold_test/hold_test.services.yml | 5 + .../EventSubscriber/HoldTestSubscriber.php | 52 ++++++++ .../Ajax/ThrobberTest.php | 112 ++++++++++++++++++ 10 files changed, 270 insertions(+), 12 deletions(-) create mode 100644 core/modules/system/tests/modules/hold_test/hold_test.info.yml create mode 100644 core/modules/system/tests/modules/hold_test/hold_test.install create mode 100644 core/modules/system/tests/modules/hold_test/hold_test.module create mode 100644 core/modules/system/tests/modules/hold_test/hold_test.services.yml create mode 100644 core/modules/system/tests/modules/hold_test/src/EventSubscriber/HoldTestSubscriber.php create mode 100644 core/tests/Drupal/FunctionalJavascriptTests/Ajax/ThrobberTest.php diff --git a/core/misc/ajax.es6.js b/core/misc/ajax.es6.js index a438fbc60c7e..739afa19747b 100644 --- a/core/misc/ajax.es6.js +++ b/core/misc/ajax.es6.js @@ -811,6 +811,40 @@ } }; + /** + * An animated progress throbber and container element for AJAX operations. + * + * @param {string} [message] + * (optional) The message shown on the UI. + * @return {string} + * The HTML markup for the throbber. + */ + Drupal.theme.ajaxProgressThrobber = (message) => { + // Build markup without adding extra white space since it affects rendering. + const messageMarkup = typeof message === 'string' ? Drupal.theme('ajaxProgressMessage', message) : ''; + const throbber = '<div class="throbber"> </div>'; + + return `<div class="ajax-progress ajax-progress-throbber">${throbber}${messageMarkup}</div>`; + }; + + /** + * An animated progress throbber and container element for AJAX operations. + * + * @return {string} + * The HTML markup for the throbber. + */ + Drupal.theme.ajaxProgressIndicatorFullscreen = () => '<div class="ajax-progress ajax-progress-fullscreen"> </div>'; + + /** + * Formats text accompanying the AJAX progress throbber. + * + * @param {string} message + * The message shown on the UI. + * @return {string} + * The HTML markup for the throbber. + */ + Drupal.theme.ajaxProgressMessage = message => `<div class="message">${message}</div>`; + /** * Sets the progress bar progress indicator. */ @@ -831,10 +865,7 @@ * Sets the throbber progress indicator. */ Drupal.Ajax.prototype.setProgressIndicatorThrobber = function () { - this.progress.element = $('<div class="ajax-progress ajax-progress-throbber"><div class="throbber"> </div></div>'); - if (this.progress.message) { - this.progress.element.find('.throbber').after(`<div class="message">${this.progress.message}</div>`); - } + this.progress.element = $(Drupal.theme('ajaxProgressThrobber', this.progress.message)); $(this.element).after(this.progress.element); }; @@ -842,7 +873,7 @@ * Sets the fullscreen progress indicator. */ Drupal.Ajax.prototype.setProgressIndicatorFullscreen = function () { - this.progress.element = $('<div class="ajax-progress ajax-progress-fullscreen"> </div>'); + this.progress.element = $(Drupal.theme('ajaxProgressIndicatorFullscreen')); $('body').after(this.progress.element); }; diff --git a/core/misc/ajax.js b/core/misc/ajax.js index 814d82f8f234..abe0ec2928df 100644 --- a/core/misc/ajax.js +++ b/core/misc/ajax.js @@ -368,6 +368,21 @@ function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr } }; + Drupal.theme.ajaxProgressThrobber = function (message) { + var messageMarkup = typeof message === 'string' ? Drupal.theme('ajaxProgressMessage', message) : ''; + var throbber = '<div class="throbber"> </div>'; + + return '<div class="ajax-progress ajax-progress-throbber">' + throbber + messageMarkup + '</div>'; + }; + + Drupal.theme.ajaxProgressIndicatorFullscreen = function () { + return '<div class="ajax-progress ajax-progress-fullscreen"> </div>'; + }; + + Drupal.theme.ajaxProgressMessage = function (message) { + return '<div class="message">' + message + '</div>'; + }; + Drupal.Ajax.prototype.setProgressIndicatorBar = function () { var progressBar = new Drupal.ProgressBar('ajax-progress-' + this.element.id, $.noop, this.progress.method, $.noop); if (this.progress.message) { @@ -382,15 +397,12 @@ function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr }; Drupal.Ajax.prototype.setProgressIndicatorThrobber = function () { - this.progress.element = $('<div class="ajax-progress ajax-progress-throbber"><div class="throbber"> </div></div>'); - if (this.progress.message) { - this.progress.element.find('.throbber').after('<div class="message">' + this.progress.message + '</div>'); - } + this.progress.element = $(Drupal.theme('ajaxProgressThrobber', this.progress.message)); $(this.element).after(this.progress.element); }; Drupal.Ajax.prototype.setProgressIndicatorFullscreen = function () { - this.progress.element = $('<div class="ajax-progress ajax-progress-fullscreen"> </div>'); + this.progress.element = $(Drupal.theme('ajaxProgressIndicatorFullscreen')); $('body').after(this.progress.element); }; diff --git a/core/modules/field_ui/field_ui.es6.js b/core/modules/field_ui/field_ui.es6.js index 965a2e4c36b0..cf1284171186 100644 --- a/core/modules/field_ui/field_ui.es6.js +++ b/core/modules/field_ui/field_ui.es6.js @@ -219,7 +219,7 @@ if (rowNames.length) { // Add a throbber next each of the ajaxElements. - $(ajaxElements).after('<div class="ajax-progress ajax-progress-throbber"><div class="throbber"> </div></div>'); + $(ajaxElements).after(Drupal.theme.ajaxProgressThrobber()); // Fire the Ajax update. $('input[name=refresh_rows]').val(rowNames.join(' ')); diff --git a/core/modules/field_ui/field_ui.js b/core/modules/field_ui/field_ui.js index d0e8a6d7126f..5cbeb6458b35 100644 --- a/core/modules/field_ui/field_ui.js +++ b/core/modules/field_ui/field_ui.js @@ -126,7 +126,7 @@ }); if (rowNames.length) { - $(ajaxElements).after('<div class="ajax-progress ajax-progress-throbber"><div class="throbber"> </div></div>'); + $(ajaxElements).after(Drupal.theme.ajaxProgressThrobber()); $('input[name=refresh_rows]').val(rowNames.join(' ')); $('input[data-drupal-selector="edit-refresh"]').trigger('mousedown'); diff --git a/core/modules/system/tests/modules/hold_test/hold_test.info.yml b/core/modules/system/tests/modules/hold_test/hold_test.info.yml new file mode 100644 index 000000000000..f76775142289 --- /dev/null +++ b/core/modules/system/tests/modules/hold_test/hold_test.info.yml @@ -0,0 +1,6 @@ +name: Hold test +type: module +description: 'Support testing with request/response hold.' +package: Testing +version: VERSION +core: 8.x diff --git a/core/modules/system/tests/modules/hold_test/hold_test.install b/core/modules/system/tests/modules/hold_test/hold_test.install new file mode 100644 index 000000000000..183463deb3b0 --- /dev/null +++ b/core/modules/system/tests/modules/hold_test/hold_test.install @@ -0,0 +1,14 @@ +<?php + +/** + * @file + * Install, update and uninstall functions for the hold_test module. + */ + +/** + * Implements hook_install(). + */ +function hold_test_install() { + hold_test_request(FALSE); + hold_test_response(FALSE); +} diff --git a/core/modules/system/tests/modules/hold_test/hold_test.module b/core/modules/system/tests/modules/hold_test/hold_test.module new file mode 100644 index 000000000000..867eacf90612 --- /dev/null +++ b/core/modules/system/tests/modules/hold_test/hold_test.module @@ -0,0 +1,26 @@ +<?php + +/** + * @file + * Contains functions for testing hold request/response. + */ + +/** + * Request hold. + * + * @param bool $status + * TRUE - enable hold, FALSE - disable hold. + */ +function hold_test_request($status) { + file_put_contents(\Drupal::root() . '/sites/default/files/simpletest/hold_test_request.txt', $status); +} + +/** + * Response hold. + * + * @param bool $status + * TRUE - enable hold, FALSE - disable hold. + */ +function hold_test_response($status) { + file_put_contents(\Drupal::root() . '/sites/default/files/simpletest/hold_test_response.txt', $status); +} diff --git a/core/modules/system/tests/modules/hold_test/hold_test.services.yml b/core/modules/system/tests/modules/hold_test/hold_test.services.yml new file mode 100644 index 000000000000..88e7babdff32 --- /dev/null +++ b/core/modules/system/tests/modules/hold_test/hold_test.services.yml @@ -0,0 +1,5 @@ +services: + hold_test.response: + class: Drupal\hold_test\EventSubscriber\HoldTestSubscriber + tags: + - { name: event_subscriber } diff --git a/core/modules/system/tests/modules/hold_test/src/EventSubscriber/HoldTestSubscriber.php b/core/modules/system/tests/modules/hold_test/src/EventSubscriber/HoldTestSubscriber.php new file mode 100644 index 000000000000..332f4c00b9a8 --- /dev/null +++ b/core/modules/system/tests/modules/hold_test/src/EventSubscriber/HoldTestSubscriber.php @@ -0,0 +1,52 @@ +<?php + +namespace Drupal\hold_test\EventSubscriber; + +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * Response subscriber to test hold. + */ +class HoldTestSubscriber implements EventSubscriberInterface { + + const HOLD_REQUEST = 'request'; + const HOLD_RESPONSE = 'response'; + + /** + * Request hold. + */ + public function onRequest() { + $this->hold(static::HOLD_REQUEST); + } + + /** + * Response hold. + */ + public function onRespond() { + $this->hold(static::HOLD_RESPONSE); + } + + /** + * Hold process by type. + * + * @param string $type + * Type of hold. + */ + protected function hold($type) { + $path = \Drupal::root() . "/sites/default/files/simpletest/hold_test_$type.txt"; + do { + $status = (bool) file_get_contents($path); + } while ($status && (NULL === usleep(100000))); + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() { + $events[KernelEvents::REQUEST][] = ['onRequest']; + $events[KernelEvents::RESPONSE][] = ['onRespond']; + return $events; + } + +} diff --git a/core/tests/Drupal/FunctionalJavascriptTests/Ajax/ThrobberTest.php b/core/tests/Drupal/FunctionalJavascriptTests/Ajax/ThrobberTest.php new file mode 100644 index 000000000000..e2ecaea7a468 --- /dev/null +++ b/core/tests/Drupal/FunctionalJavascriptTests/Ajax/ThrobberTest.php @@ -0,0 +1,112 @@ +<?php + +namespace Drupal\FunctionalJavascriptTests\Ajax; + +use Drupal\FunctionalJavascriptTests\DrupalSelenium2Driver; +use Drupal\FunctionalJavascriptTests\JavascriptTestBase; + +/** + * Tests the throbber. + * + * @group Ajax + */ +class ThrobberTest extends JavascriptTestBase { + + /** + * {@inheritdoc} + */ + protected $minkDefaultDriverClass = DrupalSelenium2Driver::class; + + /** + * {@inheritdoc} + */ + public static $modules = [ + 'node', + 'views', + 'views_ui', + 'views_ui_test_field', + 'hold_test', + ]; + + /** + * {@inheritdoc} + */ + public function setUp() { + parent::setUp(); + + $admin_user = $this->drupalCreateUser([ + 'administer views', + ]); + $this->drupalLogin($admin_user); + } + + /** + * Tests theming throbber element. + */ + public function testThemingThrobberElement() { + $session = $this->getSession(); + $web_assert = $this->assertSession(); + $page = $session->getPage(); + + $custom_ajax_progress_indicator_fullscreen = <<<JS + Drupal.theme.ajaxProgressIndicatorFullscreen = function () { + return '<div class="custom-ajax-progress-fullscreen"></div>'; + }; +JS; + $custom_ajax_progress_throbber = <<<JS + Drupal.theme.ajaxProgressThrobber = function (message) { + return '<div class="custom-ajax-progress-throbber"></div>'; + }; +JS; + $custom_ajax_progress_message = <<<JS + Drupal.theme.ajaxProgressMessage = function (message) { + return '<div class="custom-ajax-progress-message">Hold door!</div>'; + }; +JS; + + $this->drupalGet('admin/structure/views/view/content'); + $this->waitForNoElement('.ajax-progress-fullscreen'); + + // Test theming fullscreen throbber. + $session->executeScript($custom_ajax_progress_indicator_fullscreen); + hold_test_response(TRUE); + $page->clickLink('Content: Published (grouped)'); + $this->assertNotNull($web_assert->waitForElement('css', '.custom-ajax-progress-fullscreen'), 'Custom ajaxProgressIndicatorFullscreen.'); + hold_test_response(FALSE); + $this->waitForNoElement('.custom-ajax-progress-fullscreen'); + + // Test theming throbber message. + $web_assert->waitForElementVisible('css', '[data-drupal-selector="edit-options-group-info-add-group"]'); + $session->executeScript($custom_ajax_progress_message); + hold_test_response(TRUE); + $page->pressButton('Add another item'); + $this->assertNotNull($web_assert->waitForElement('css', '.ajax-progress-throbber .custom-ajax-progress-message'), 'Custom ajaxProgressMessage.'); + hold_test_response(FALSE); + $this->waitForNoElement('.ajax-progress-throbber'); + + // Test theming throbber. + $web_assert->waitForElementVisible('css', '[data-drupal-selector="edit-options-group-info-group-items-3-title"]'); + $session->executeScript($custom_ajax_progress_throbber); + hold_test_response(TRUE); + $page->pressButton('Add another item'); + $this->assertNotNull($web_assert->waitForElement('css', '.custom-ajax-progress-throbber'), 'Custom ajaxProgressThrobber.'); + hold_test_response(FALSE); + $this->waitForNoElement('.custom-ajax-progress-throbber'); + } + + /** + * Waits for an element to be removed from the page. + * + * @param string $selector + * CSS selector. + * @param int $timeout + * (optional) Timeout in milliseconds, defaults to 10000. + * + * @todo Remove in https://www.drupal.org/node/2892440. + */ + protected function waitForNoElement($selector, $timeout = 10000) { + $condition = "(typeof jQuery !== 'undefined' && jQuery('$selector').length === 0)"; + $this->assertJsCondition($condition, $timeout); + } + +} -- GitLab