From 8496c2c3e4b3e3d1e8a2d9b7afad6b928fc831bb Mon Sep 17 00:00:00 2001 From: nod_ <nod_@598310.no-reply.drupal.org> Date: Fri, 31 Mar 2023 11:19:12 +0200 Subject: [PATCH] Issue #3110517 by gapple, andypost, nod_, Wim Leers, smustgrave: Improve Drupal\Core\Ajax\AddCssCommand to accept an array of CSS assets --- core/lib/Drupal/Core/Ajax/AddCssCommand.php | 18 ++-- .../Ajax/AjaxResponseAttachmentsProcessor.php | 2 +- core/misc/ajax.js | 45 ++++++++- .../ajax_forms_test/ajax_forms_test.module | 18 +++- .../src/Form/AjaxFormsTestCommandsForm.php | 10 ++ .../src/Functional/Ajax/FrameworkTest.php | 2 +- .../Ajax/CommandsTest.php | 19 ++++ .../Tests/Core/Ajax/AjaxCommandsTest.php | 97 ++++++++++++++++++- 8 files changed, 193 insertions(+), 18 deletions(-) diff --git a/core/lib/Drupal/Core/Ajax/AddCssCommand.php b/core/lib/Drupal/Core/Ajax/AddCssCommand.php index 80ee17aa0334..84c581013822 100644 --- a/core/lib/Drupal/Core/Ajax/AddCssCommand.php +++ b/core/lib/Drupal/Core/Ajax/AddCssCommand.php @@ -15,30 +15,30 @@ class AddCssCommand implements CommandInterface { /** - * A string that contains the styles to be added to the page. + * Arrays containing attributes of the stylesheets to be added to the page. * - * It should include the wrapping style tag. - * - * @var string + * @var string[][]|string */ protected $styles; /** * Constructs an AddCssCommand. * - * @param string $styles - * A string that contains the styles to be added to the page, including the - * wrapping <style> tag. + * @param string[][]|string $styles + * Arrays containing attributes of the stylesheets to be added to the page. + * i.e. `['href' => 'someURL']` becomes `<link href="someURL">`. */ public function __construct($styles) { + if (is_string($styles)) { + @trigger_error('The ' . __NAMESPACE__ . '\AddCssCommand with a string argument is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. See http://www.drupal.org/node/3154948', E_USER_DEPRECATED); + } $this->styles = $styles; } /** - * Implements Drupal\Core\Ajax\CommandInterface:render(). + * {@inheritdoc} */ public function render() { - return [ 'command' => 'add_css', 'data' => $this->styles, diff --git a/core/lib/Drupal/Core/Ajax/AjaxResponseAttachmentsProcessor.php b/core/lib/Drupal/Core/Ajax/AjaxResponseAttachmentsProcessor.php index 3936ce8fe0db..a96e0e14dcff 100644 --- a/core/lib/Drupal/Core/Ajax/AjaxResponseAttachmentsProcessor.php +++ b/core/lib/Drupal/Core/Ajax/AjaxResponseAttachmentsProcessor.php @@ -174,7 +174,7 @@ protected function buildAttachmentsCommands(AjaxResponse $response, Request $req $resource_commands = []; if ($css_assets) { $css_render_array = $this->cssCollectionRenderer->render($css_assets); - $resource_commands[] = new AddCssCommand($this->renderer->renderPlain($css_render_array)); + $resource_commands[] = new AddCssCommand(array_column($css_render_array, '#attributes')); } if ($js_assets_header) { $js_header_render_array = $this->jsCollectionRenderer->render($js_assets_header); diff --git a/core/misc/ajax.js b/core/misc/ajax.js index cb416174f720..ec18625bf656 100644 --- a/core/misc/ajax.js +++ b/core/misc/ajax.js @@ -1659,13 +1659,52 @@ * {@link Drupal.Ajax} object created by {@link Drupal.ajax}. * @param {object} response * The response from the Ajax request. - * @param {string} response.data - * A string that contains the styles to be added. + * @param {object[]|string} response.data + * An array of styles to be added. * @param {number} [status] * The XMLHttpRequest status. */ add_css(ajax, response, status) { - $('head').prepend(response.data); + if (typeof response.data === 'string') { + Drupal.deprecationError({ + message: + 'Passing a string to the Drupal.ajax.add_css() method is deprecated in 10.1.0 and is removed from drupal:11.0.0. See https://www.drupal.org/node/3154948.', + }); + $('head').prepend(response.data); + return; + } + + const allUniqueBundleIds = response.data.map(function (style) { + const uniqueBundleId = style.href + ajax.instanceIndex; + loadjs(style.href, uniqueBundleId, { + before(path, styleEl) { + // This allows all attributes to be added, like media. + Object.keys(style).forEach((attributeKey) => { + styleEl.setAttribute(attributeKey, style[attributeKey]); + }); + }, + }); + return uniqueBundleId; + }); + // Returns the promise so that the next AJAX command waits on the + // completion of this one to execute, ensuring the CSS is loaded before + // executing. + return new Promise((resolve, reject) => { + loadjs.ready(allUniqueBundleIds, { + success() { + // All CSS files were loaded. Resolve the promise and let the + // remaining commands execute. + resolve(); + }, + error(depsNotFound) { + const message = Drupal.t( + `The following files could not be loaded: @dependencies`, + { '@dependencies': depsNotFound.join(', ') }, + ); + reject(message); + }, + }); + }); }, /** diff --git a/core/modules/system/tests/modules/ajax_forms_test/ajax_forms_test.module b/core/modules/system/tests/modules/ajax_forms_test/ajax_forms_test.module index 7896856a4b0d..30ca6bb834c0 100644 --- a/core/modules/system/tests/modules/ajax_forms_test/ajax_forms_test.module +++ b/core/modules/system/tests/modules/ajax_forms_test/ajax_forms_test.module @@ -202,7 +202,23 @@ function ajax_forms_test_advanced_commands_settings_callback($form, FormStateInt */ function ajax_forms_test_advanced_commands_add_css_callback($form, FormStateInterface $form_state) { $response = new AjaxResponse(); - $response->addCommand(new AddCssCommand('my/file.css')); + $response->addCommand(new AddCssCommand([ + [ + 'href' => 'my/file.css', + 'media' => 'all', + ], + ])); + return $response; +} + +/** + * Ajax callback for 'add_css' that uses legacy string parameter. + * + * @todo Remove in Drupal 11.0.0 https://www.drupal.org/i/3339374 + */ +function ajax_forms_test_advanced_commands_add_css_legacy_callback($form, FormStateInterface $form_state) { + $response = new AjaxResponse(); + $response->addCommand(new AddCssCommand("<link rel='stylesheet' href='my/file.css' media='all' />")); return $response; } diff --git a/core/modules/system/tests/modules/ajax_forms_test/src/Form/AjaxFormsTestCommandsForm.php b/core/modules/system/tests/modules/ajax_forms_test/src/Form/AjaxFormsTestCommandsForm.php index 5532b3d06d68..8c1e205afbb5 100644 --- a/core/modules/system/tests/modules/ajax_forms_test/src/Form/AjaxFormsTestCommandsForm.php +++ b/core/modules/system/tests/modules/ajax_forms_test/src/Form/AjaxFormsTestCommandsForm.php @@ -224,6 +224,16 @@ public function buildForm(array $form, FormStateInterface $form_state) { ], ]; + // Shows the Ajax 'add_css' command with legacy string parameter. + // @todo Remove in Drupal 11.0.0 https://www.drupal.org/i/3339374 + $form['add_css_legacy_command_example'] = [ + '#type' => 'submit', + '#value' => $this->t("AJAX 'add_css' legacy command"), + '#ajax' => [ + 'callback' => 'ajax_forms_test_advanced_commands_add_css_legacy_callback', + ], + ]; + $form['submit'] = [ '#type' => 'submit', '#value' => $this->t('Submit'), diff --git a/core/modules/system/tests/src/Functional/Ajax/FrameworkTest.php b/core/modules/system/tests/src/Functional/Ajax/FrameworkTest.php index ccd21ca523c7..afa6f8df54d0 100644 --- a/core/modules/system/tests/src/Functional/Ajax/FrameworkTest.php +++ b/core/modules/system/tests/src/Functional/Ajax/FrameworkTest.php @@ -53,7 +53,7 @@ public function testOrder() { $build['#attached']['library'][] = 'ajax_test/order-css-command'; $assets = AttachedAssets::createFromRenderArray($build); $css_render_array = $css_collection_renderer->render($asset_resolver->getCssAssets($assets, FALSE, \Drupal::languageManager()->getCurrentLanguage())); - $expected_commands[1] = new AddCssCommand($renderer->renderRoot($css_render_array)); + $expected_commands[1] = new AddCssCommand(array_column($css_render_array, '#attributes')); $build['#attached']['library'][] = 'ajax_test/order-header-js-command'; $build['#attached']['library'][] = 'ajax_test/order-footer-js-command'; $assets = AttachedAssets::createFromRenderArray($build); diff --git a/core/tests/Drupal/FunctionalJavascriptTests/Ajax/CommandsTest.php b/core/tests/Drupal/FunctionalJavascriptTests/Ajax/CommandsTest.php index 5f4b48bf8bd7..6c4621431d00 100644 --- a/core/tests/Drupal/FunctionalJavascriptTests/Ajax/CommandsTest.php +++ b/core/tests/Drupal/FunctionalJavascriptTests/Ajax/CommandsTest.php @@ -140,6 +140,25 @@ public function testAjaxCommands() { $this->assertWaitPageContains('<div class="test-settings-command">42</div>'); } + /** + * Tests the various Ajax Commands with legacy parameters. + * @group legacy + */ + public function testLegacyAjaxCommands() { + $session = $this->getSession(); + $page = $this->getSession()->getPage(); + + $form_path = 'ajax_forms_test_ajax_commands_form'; + $web_user = $this->drupalCreateUser(['access content']); + $this->drupalLogin($web_user); + $this->drupalGet($form_path); + + // Tests the 'add_css' command with legacy string value. + $this->expectDeprecation('Javascript Deprecation: Passing a string to the Drupal.ajax.add_css() method is deprecated in 10.1.0 and is removed from drupal:11.0.0. See https://www.drupal.org/node/3154948.'); + $page->pressButton("AJAX 'add_css' legacy command"); + $this->assertWaitPageContains('my/file.css'); + } + /** * Asserts that page contains a text after waiting. * diff --git a/core/tests/Drupal/Tests/Core/Ajax/AjaxCommandsTest.php b/core/tests/Drupal/Tests/Core/Ajax/AjaxCommandsTest.php index c482a0211976..90169e978e4a 100644 --- a/core/tests/Drupal/Tests/Core/Ajax/AjaxCommandsTest.php +++ b/core/tests/Drupal/Tests/Core/Ajax/AjaxCommandsTest.php @@ -36,15 +36,106 @@ */ class AjaxCommandsTest extends UnitTestCase { + /** + * @return array + * - Array of css elements + * - Expected value + */ + public function providerCss() { + return [ + 'empty' => [ + [], + [ + 'command' => 'add_css', + 'data' => [], + ], + ], + 'single' => [ + [ + [ + 'href' => 'core/misc/example.css', + 'media' => 'all', + ], + ], + [ + 'command' => 'add_css', + 'data' => [ + [ + 'href' => 'core/misc/example.css', + 'media' => 'all', + ], + ], + ], + ], + 'single-data-property' => [ + [ + [ + 'href' => 'core/misc/example.css', + 'media' => 'all', + 'data-test' => 'test', + ], + ], + [ + 'command' => 'add_css', + 'data' => [ + [ + 'href' => 'core/misc/example.css', + 'media' => 'all', + 'data-test' => 'test', + ], + ], + ], + ], + 'multiple' => [ + [ + [ + 'href' => 'core/misc/example1.css', + 'media' => 'all', + ], + [ + 'href' => 'core/misc/example2.css', + 'media' => 'all', + ], + ], + [ + 'command' => 'add_css', + 'data' => [ + [ + 'href' => 'core/misc/example1.css', + 'media' => 'all', + ], + [ + 'href' => 'core/misc/example2.css', + 'media' => 'all', + ], + ], + ], + ], + ]; + } + /** * @covers \Drupal\Core\Ajax\AddCssCommand + * @dataProvider providerCss */ - public function testAddCssCommand() { - $command = new AddCssCommand('p{ text-decoration:blink; }'); + public function testAddCssCommand($css, $expected) { + $command = new AddCssCommand($css); + + $this->assertEquals($expected, $command->render()); + } + + /** + * @covers \Drupal\Core\Ajax\AddCssCommand + * @group legacy + */ + public function testStringAddCssCommand() { + $this->expectDeprecation("The Drupal\Core\Ajax\AddCssCommand with a string argument is deprecated in drupal:10.1.0 and is removed from drupal:11.0.0. See http://www.drupal.org/node/3154948"); + + $command = new AddCssCommand('<style>p{ text-decoration:blink; }</style>'); $expected = [ 'command' => 'add_css', - 'data' => 'p{ text-decoration:blink; }', + 'data' => '<style>p{ text-decoration:blink; }</style>', ]; $this->assertEquals($expected, $command->render()); -- GitLab