diff --git a/core/misc/ajax.es6.js b/core/misc/ajax.es6.js index 6da787bba8..d8fe6e8783 100644 --- a/core/misc/ajax.es6.js +++ b/core/misc/ajax.es6.js @@ -980,10 +980,43 @@ }; /** - * Provide a wrapper for multiple root elements via Ajax loading. + * Provide a wrapper for new content via Ajax. + * + * @param {jQuery} $newContent + * Response elements after parsing. + * @param {Drupal.Ajax} ajax + * {@link Drupal.Ajax} object created by {@link Drupal.ajax}. + * @param {object} response + * The response from the Ajax request. + */ + Drupal.theme.ajaxWrapperNewContent = ($newContent, ajax, response) => { + let typeEffect = response.effect || ajax.effect; + if ($newContent.length > 1 && typeEffect !== 'none') { + let countEffectElements = $newContent.length; + // Exclude all nodes not participating in the animation effect. + for (let i = 0; i < $newContent.length; i++) { + if ( + ($newContent[i].nodeName === '#comment') || + ($newContent[i].nodeName === '#text' && /^(\s|\n|\r)*$/.test($newContent[i].textContent)) + ) { + countEffectElements--; + } + } + if (countEffectElements > 1) { + $newContent = Drupal.theme('ajaxWrapperMultipleRootElements', $newContent); + } + } + return $newContent; + }; + + /** + * Provide a wrapper for multiple root elements via Ajax. + * + * @param {jQuery} $elements + * Response elements after parsing. */ - Drupal.theme.ajaxWrapperMultipleRootElements = (elements) => { - return elements; + Drupal.theme.ajaxWrapperMultipleRootElements = ($elements) => { + return $('
').append($elements); }; /** @@ -1045,10 +1078,7 @@ // Parse response.data into an element collection. let $newContent = $('
').html(response.data).contents(); - // @todo: Remove after provide cool animation for single/multiple root. - if ($newContent.length > 1) { - $newContent = Drupal.theme('ajaxWrapperMultipleRootElements', $newContent); - } + $newContent = Drupal.theme('ajaxWrapperNewContent', $newContent, ajax, response); // If removing content from the wrapper, detach behaviors first. switch (method) { diff --git a/core/misc/ajax.js b/core/misc/ajax.js index f4e199fe97..d614a61d0a 100644 --- a/core/misc/ajax.js +++ b/core/misc/ajax.js @@ -478,8 +478,25 @@ function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr throw new Drupal.AjaxError(xmlhttprequest, uri, customMessage); }; - Drupal.theme.ajaxWrapperMultipleRootElements = function (elements) { - return elements; + Drupal.theme.ajaxWrapperNewContent = function ($newContent, ajax, response) { + var typeEffect = response.effect || ajax.effect; + if ($newContent.length > 1 && typeEffect !== 'none') { + var countEffectElements = $newContent.length; + + for (var i = 0; i < $newContent.length; i++) { + if ($newContent[i].nodeName === '#comment' || $newContent[i].nodeName === '#text' && /^(\s|\n|\r)*$/.test($newContent[i].textContent)) { + countEffectElements--; + } + } + if (countEffectElements > 1) { + $newContent = Drupal.theme('ajaxWrapperMultipleRootElements', $newContent); + } + } + return $newContent; + }; + + Drupal.theme.ajaxWrapperMultipleRootElements = function ($elements) { + return $('
').append($elements); }; Drupal.AjaxCommands = function () {}; @@ -492,10 +509,7 @@ function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr var settings = response.settings || ajax.settings || drupalSettings; var $newContent = $('
').html(response.data).contents(); - - if ($newContent.length > 1) { - $newContent = Drupal.theme('ajaxWrapperMultipleRootElements', $newContent); - } + $newContent = Drupal.theme('ajaxWrapperNewContent', $newContent, ajax, response); switch (method) { case 'html': diff --git a/core/modules/system/tests/modules/ajax_test/js/insert-ajax.es6.js b/core/modules/system/tests/modules/ajax_test/js/insert-ajax.es6.js index e7fb3f212b..d25552a8bd 100644 --- a/core/modules/system/tests/modules/ajax_test/js/insert-ajax.es6.js +++ b/core/modules/system/tests/modules/ajax_test/js/insert-ajax.es6.js @@ -15,6 +15,7 @@ base: false, element: false, method: event.currentTarget.getAttribute('data-method'), + effect: event.currentTarget.getAttribute('data-effect'), }; const myAjaxObject = Drupal.ajax(ajaxSettings); myAjaxObject.execute(); diff --git a/core/modules/system/tests/modules/ajax_test/js/insert-ajax.js b/core/modules/system/tests/modules/ajax_test/js/insert-ajax.js index c261bca80a..0f96097313 100644 --- a/core/modules/system/tests/modules/ajax_test/js/insert-ajax.js +++ b/core/modules/system/tests/modules/ajax_test/js/insert-ajax.js @@ -15,7 +15,8 @@ wrapper: 'ajax-target', base: false, element: false, - method: event.currentTarget.getAttribute('data-method') + method: event.currentTarget.getAttribute('data-method'), + effect: event.currentTarget.getAttribute('data-effect') }; var myAjaxObject = Drupal.ajax(ajaxSettings); myAjaxObject.execute(); diff --git a/core/modules/system/tests/modules/ajax_test/src/Controller/AjaxTestController.php b/core/modules/system/tests/modules/ajax_test/src/Controller/AjaxTestController.php index 9b6a9e2cef..24de8b00a1 100644 --- a/core/modules/system/tests/modules/ajax_test/src/Controller/AjaxTestController.php +++ b/core/modules/system/tests/modules/ajax_test/src/Controller/AjaxTestController.php @@ -56,10 +56,11 @@ public function renderTypes($type) { '#title' => 'AJAX Dialog & contents', 'content' => [ '#type' => 'inline_template', - '#template' => $this->getRenderTypes()[$type], + '#template' => $this->getRenderTypes()[$type]['render'], ], ]; + $content['effect'] = 'fade'; return $content; } @@ -85,7 +86,7 @@ public function insertLinksBlockWrapper() { ], ]; foreach ($methods as $method) { - foreach (array_keys($this->getRenderTypes()) as $type) { + foreach ($this->getRenderTypes() as $type => $item) { $class = 'ajax-insert'; $build['links']['links']['#links']["$method-$type"] = [ 'title' => "Link $method $type", @@ -93,6 +94,7 @@ public function insertLinksBlockWrapper() { 'attributes' => [ 'class' => [$class], 'data-method' => $method, + 'data-effect' => $item['effect'], ], ]; } @@ -122,7 +124,7 @@ public function insertLinksInlineWrapper() { ], ]; foreach ($methods as $method) { - foreach (array_keys($this->getRenderTypes()) as $type) { + foreach ($this->getRenderTypes() as $type => $item) { $class = 'ajax-insert-inline'; $build['links']['links']['#links']["$method-$type"] = [ 'title' => "Link $method $type", @@ -130,6 +132,7 @@ public function insertLinksInlineWrapper() { 'attributes' => [ 'class' => [$class], 'data-method' => $method, + 'data-effect' => $item['effect'], ], ]; } @@ -324,7 +327,7 @@ public function dialogClose() { * Render types. */ protected function getRenderTypes() { - return [ + $render_types = [ 'pre-wrapped-div' => '
pre-wrapped
', 'pre-wrapped-span' => 'pre-wrapped', 'pre-wrapped-whitespace' => '
pre-wrapped-whitespace
' . "\r\n", @@ -339,6 +342,11 @@ protected function getRenderTypes() { 'svg' => '', 'empty' => '', ]; + foreach ($render_types as $key => $render) { + $render_info[$key] = ['render' => $render, 'effect' => 'fade']; + $render_info["$key--effect-none"] = ['render' => $render, 'effect' => 'none']; + } + return $render_info; } } diff --git a/core/tests/Drupal/FunctionalJavascriptTests/Ajax/AjaxTest.php b/core/tests/Drupal/FunctionalJavascriptTests/Ajax/AjaxTest.php index 201e6b5925..7a63551c36 100644 --- a/core/tests/Drupal/FunctionalJavascriptTests/Ajax/AjaxTest.php +++ b/core/tests/Drupal/FunctionalJavascriptTests/Ajax/AjaxTest.php @@ -131,10 +131,12 @@ public function testInsertAjaxResponse() { // Test that empty response data. $test_cases['empty'] = ''; + foreach ($test_cases as $key => $render) { + $test_all_cases["$key--effect-none"] = $render; + } + $test_cases = $test_all_cases; + $multiple_root_cases = [ - 'pre-wrapped-whitespace', - 'comment-string-not-wrapped', - 'comment-not-wrapped', 'mixed', 'top-level-only', 'top-level-only-pre-whitespace', @@ -142,7 +144,7 @@ public function testInsertAjaxResponse() { 'top-level-only-middle-whitespace-span', ]; - $wrapper_multiple_root_elements = <<').append(elements); @@ -151,16 +153,37 @@ public function testInsertAjaxResponse() { JS; foreach ($test_cases as $render_type => $expected) { + if ( + strpos($render_type, '--effect-none') === FALSE && + in_array($render_type, $multiple_root_cases, TRUE) + ) { + $unprocessed_expected = str_replace([' class="processed"', ' processed'], '', $expected); + $expected = '
' . $unprocessed_expected . '
'; + $expected_custom_wrapper = '
' . $unprocessed_expected . '
'; + } + else { + $expected_custom_wrapper = $expected; + } + + // Checking default process of wrapping Ajax content. $this->assertInsertBlock($render_type, $expected); $this->assertInsertInline($render_type, $expected); - // Test that wrapper for multiple root elements works only for targets. - if (in_array($render_type, $multiple_root_cases, TRUE)) { - $expected = '
' . str_replace([' class="processed"', ' processed'], '', $expected) . '
'; - } - $this->assertInsertBlock($render_type, $expected, $wrapper_multiple_root_elements); - $this->assertInsertInline($render_type, $expected, $wrapper_multiple_root_elements); + // Checking custom logic for wrapping Ajax content with multiple root. + $this->assertInsertBlock($render_type, $expected_custom_wrapper, $custom_wrapper); + $this->assertInsertInline($render_type, $expected_custom_wrapper, $custom_wrapper); } + + // Checking that we can fully control the process of wrapping Ajax content. + $custom_wrapper_new_content = <<').append(elements); + }; + }(jQuery, Drupal)); +JS; + $this->assertInsertBlock('empty--effect-none', '
', $custom_wrapper_new_content); + $this->assertInsertInline('empty--effect-none', '
', $custom_wrapper_new_content); } /**