From d90d0ebbda976d7bd18b49bddef989de51d20ed5 Mon Sep 17 00:00:00 2001 From: Alex Pott <alex.a.pott@googlemail.com> Date: Sun, 23 Jun 2019 12:49:55 +0100 Subject: [PATCH] Issue #2979044 by BR0kEN, alexpott: Unable to change menu items weight when the "system.site.weight_select_max" exceeded and "#pre_render" for "number" element is modified --- .../lib/Drupal/Core/Render/Element/Weight.php | 16 ++-- .../element_info_test.info.yml | 5 ++ .../element_info_test.module | 24 ++++++ .../Core/Render/Element/WeightTest.php | 76 ++++++++++++++++++- 4 files changed, 110 insertions(+), 11 deletions(-) create mode 100644 core/modules/system/tests/modules/element_info_test/element_info_test.info.yml create mode 100644 core/modules/system/tests/modules/element_info_test/element_info_test.module diff --git a/core/lib/Drupal/Core/Render/Element/Weight.php b/core/lib/Drupal/Core/Render/Element/Weight.php index 649de315fd72..029c9e4a4975 100644 --- a/core/lib/Drupal/Core/Render/Element/Weight.php +++ b/core/lib/Drupal/Core/Render/Element/Weight.php @@ -45,16 +45,16 @@ public function getInfo() { } /** - * Expands a weight element into a select element. + * Expands a weight element into a select/number element. */ public static function processWeight(&$element, FormStateInterface $form_state, &$complete_form) { + // If the number of options is small enough, use a select field. Otherwise, + // use a number field. + $type = $element['#delta'] <= \Drupal::config('system.site')->get('weight_select_max') ? 'select' : 'number'; + $element = array_merge($element, \Drupal::service('element_info')->getInfo($type)); $element['#is_weight'] = TRUE; - $element_info_manager = \Drupal::service('element_info'); - // If the number of options is small enough, use a select field. - $max_elements = \Drupal::config('system.site')->get('weight_select_max'); - if ($element['#delta'] <= $max_elements) { - $element['#type'] = 'select'; + if ($type === 'select') { $weights = []; for ($n = (-1 * $element['#delta']); $n <= $element['#delta']; $n++) { $weights[$n] = $n; @@ -65,14 +65,10 @@ public static function processWeight(&$element, FormStateInterface $form_state, ksort($weights); } $element['#options'] = $weights; - $element += $element_info_manager->getInfo('select'); } - // Otherwise, use a text field. else { - $element['#type'] = 'number'; // Use a field big enough to fit most weights. $element['#size'] = 10; - $element += $element_info_manager->getInfo('number'); } return $element; diff --git a/core/modules/system/tests/modules/element_info_test/element_info_test.info.yml b/core/modules/system/tests/modules/element_info_test/element_info_test.info.yml new file mode 100644 index 000000000000..9e43148ee610 --- /dev/null +++ b/core/modules/system/tests/modules/element_info_test/element_info_test.info.yml @@ -0,0 +1,5 @@ +name: 'Element info test' +type: module +package: Testing +version: VERSION +core: 8.x diff --git a/core/modules/system/tests/modules/element_info_test/element_info_test.module b/core/modules/system/tests/modules/element_info_test/element_info_test.module new file mode 100644 index 000000000000..58ebc889e2a0 --- /dev/null +++ b/core/modules/system/tests/modules/element_info_test/element_info_test.module @@ -0,0 +1,24 @@ +<?php + +/** + * @file + * Element info test. + */ + +/** + * Implements hook_element_info_alter(). + */ +function element_info_test_element_info_alter(array &$info) { + $info['number'] += ['#pre_render' => []]; + /* @see \Drupal\KernelTests\Core\Render\Element\WeightTest::testProcessWeightSelectMax() */ + $info['number']['#pre_render'][] = 'element_info_test_element_pre_render'; +} + +/** + * {@inheritdoc} + * + * @see \Drupal\KernelTests\Core\Render\Element\WeightTest::testProcessWeightSelectMax() + */ +function element_info_test_element_pre_render(array $element) { + return $element; +} diff --git a/core/tests/Drupal/KernelTests/Core/Render/Element/WeightTest.php b/core/tests/Drupal/KernelTests/Core/Render/Element/WeightTest.php index 123c5e652145..b44b98cf1283 100644 --- a/core/tests/Drupal/KernelTests/Core/Render/Element/WeightTest.php +++ b/core/tests/Drupal/KernelTests/Core/Render/Element/WeightTest.php @@ -3,6 +3,8 @@ namespace Drupal\KernelTests\Core\Render\Element; use Drupal\Core\Form\FormState; +use Drupal\Core\Render\Element\Number; +use Drupal\Core\Render\Element\Select; use Drupal\Core\Render\Element\Weight; use Drupal\KernelTests\KernelTestBase; @@ -15,7 +17,7 @@ class WeightTest extends KernelTestBase { /** * {@inheritdoc} */ - protected static $modules = ['system']; + protected static $modules = ['system', 'element_info_test']; /** * {@inheritdoc} @@ -49,4 +51,76 @@ public function testProcessWeight() { ); } + /** + * Test transformation from "select" to "number" for MAX_DELTA + 1. + * + * @throws \Exception + * + * @covers ::processWeight + */ + public function testProcessWeightSelectMax() { + $form_state = new FormState(); + $definition = [ + '#type' => 'weight', + '#delta' => $this->container + ->get('config.factory') + ->get('system.site') + ->get('weight_select_max'), + // Expected by the "doBuildForm()" method of "form_builder" service. + '#parents' => [], + ]; + + $assert = function ($type, array $element, array $expected) use ($form_state) { + // Pretend we have a form to trigger the "#process" callbacks. + $element = $this->container + ->get('form_builder') + ->doBuildForm(__FUNCTION__, $element, $form_state); + + $expected['#type'] = $type; + + foreach ($expected as $property => $value) { + static::assertSame($value, $element[$property]); + } + + return $element; + }; + + // When the "#delta" is less or equal to maximum the "weight" must be + // rendered as a "select". + $select = $definition; + $assert('select', $select, [ + '#process' => [ + [Select::class, 'processSelect'], + [Select::class, 'processAjaxForm'], + ], + '#pre_render' => [ + [Select::class, 'preRenderSelect'], + ], + ]); + + $number = $definition; + // Increase "#delta" in order to start rendering "number" elements + // instead of "select". + $number['#delta']++; + // The "number" element definition has the "#pre_render" declaration by + // default. The "hook_element_info_alter()" allows to modify the definition + // of an element. We must be sure the standard "#pre_render" callbacks + // are presented (unless explicitly removed) even in a case when the array + // is modified by the alter hook. + $assert('number', $number, [ + '#process' => [ + [Number::class, 'processAjaxForm'], + ], + '#element_validate' => [ + [Number::class, 'validateNumber'], + ], + '#pre_render' => [ + [Number::class, 'preRenderNumber'], + // The custom callback is appended. + /* @see element_info_test_element_info_alter() */ + 'element_info_test_element_pre_render', + ], + ]); + } + } -- GitLab