From 5e5839ab59d99d853c86e894f6328fb48e38a63f Mon Sep 17 00:00:00 2001 From: nod_ <nod_@598310.no-reply.drupal.org> Date: Tue, 26 Mar 2024 16:54:43 +0100 Subject: [PATCH] Issue #1149078 by vasike, wuinfo - Bill Wu, dww, Gauravvvv, Marios Anagnostopoulos, peterpoe, VladimirAus, legolasbo, GoZ, mgifford, dalin, jrb, heddn, himanshupathak3, _utsavsharma, GrandmaGlassesRopeMan, arcaneadam, danflanagan8, arnaud-brugnon, Daniel Kulbe, uzlov, Kuntyi, DamienMcKenna, Neslee Canil Pinto, COBadger, jofitz, esod, sukanya.ramakrishnan, nod_, droplet, smustgrave, CKIDOW: States API doesn't work with multiple select fields (cherry picked from commit f6be8531a8849fe9fce6925594b90278c1e87f2b) --- core/misc/states.js | 10 ++ .../src/Form/JavascriptStatesForm.php | 118 ++++++++++++++++++ .../Core/Form/JavascriptStatesTest.php | 58 +++++++++ 3 files changed, 186 insertions(+) diff --git a/core/misc/states.js b/core/misc/states.js index a55c4d08db00..3109e1f1bdb0 100644 --- a/core/misc/states.js +++ b/core/misc/states.js @@ -149,6 +149,7 @@ * * @prop {function} RegExp * @prop {function} Function + * @prop {function} Array * @prop {function} Number */ states.Dependent.comparisons = { @@ -159,6 +160,15 @@ // The "reference" variable is a comparison function. return reference(value); }, + Array(reference, value) { + // Make sure value is an array. + if (!Array.isArray(value)) { + return false; + } + + // The arrays values should match. + return JSON.stringify(reference.sort()) === JSON.stringify(value.sort()); + }, Number(reference, value) { // If "reference" is a number and "value" is a string, then cast // reference as a string before applying the strict comparison in diff --git a/core/modules/system/tests/modules/form_test/src/Form/JavascriptStatesForm.php b/core/modules/system/tests/modules/form_test/src/Form/JavascriptStatesForm.php index bf367003d105..0677df3bd06e 100644 --- a/core/modules/system/tests/modules/form_test/src/Form/JavascriptStatesForm.php +++ b/core/modules/system/tests/modules/form_test/src/Form/JavascriptStatesForm.php @@ -24,6 +24,12 @@ public function getFormId() { * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state) { + + $form['header_triggers'] = [ + '#type' => 'html_tag', + '#tag' => 'h2', + '#value' => 'Triggers', + ]; $form['checkbox_trigger'] = [ '#type' => 'checkbox', '#title' => 'Checkbox trigger', @@ -112,8 +118,30 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#title' => 'Number trigger', ]; + $form['multiple_select_trigger'] = [ + '#type' => 'select', + '#multiple' => TRUE, + '#title' => 'Multiple select trigger', + '#options' => [ + 'value1' => 'Value 1', + 'value2' => 'Value 2', + 'value3' => 'Value 3', + ], + ]; + // Tested fields. + $form['header_tested_elements'] = [ + '#type' => 'html_tag', + '#tag' => 'h2', + '#value' => 'Tested elements', + ]; + // Checkbox trigger. + $form['header_checkbox'] = [ + '#type' => 'html_tag', + '#tag' => 'h3', + '#value' => 'Checkbox tests', + ]; $form['textfield_invisible_when_checkbox_trigger_checked'] = [ '#type' => 'textfield', '#title' => 'Textfield invisible when checkbox trigger checked', @@ -318,6 +346,11 @@ public function buildForm(array $form, FormStateInterface $form_state) { ]; // Checkboxes trigger. + $form['header_checkboxes'] = [ + '#type' => 'html_tag', + '#tag' => 'h3', + '#value' => 'Checkboxes tests', + ]; $form['textfield_visible_when_checkboxes_trigger_value2_checked'] = [ '#type' => 'textfield', '#title' => 'Textfield visible when checkboxes trigger value2 checked', @@ -338,6 +371,11 @@ public function buildForm(array $form, FormStateInterface $form_state) { ]; // Radios trigger. + $form['header_radios'] = [ + '#type' => 'html_tag', + '#tag' => 'h3', + '#value' => 'Radios tests', + ]; $form['fieldset_visible_when_radios_trigger_has_value2'] = [ '#type' => 'fieldset', '#title' => 'Fieldset visible when radio trigger has value2', @@ -407,6 +445,11 @@ public function buildForm(array $form, FormStateInterface $form_state) { ]; // Select trigger + $form['header_select'] = [ + '#type' => 'html_tag', + '#tag' => 'h3', + '#value' => 'Select tests', + ]; $form['item_visible_when_select_trigger_has_value2'] = [ '#type' => 'item', '#title' => 'Item visible when select trigger has value2', @@ -438,7 +481,72 @@ public function buildForm(array $form, FormStateInterface $form_state) { ], ]; + // Multiple select trigger. + $form['header_multiple_select'] = [ + '#type' => 'html_tag', + '#tag' => 'h3', + '#value' => 'Multiple select tests', + ]; + $form['item_visible_when_multiple_select_trigger_has_value2'] = [ + '#type' => 'item', + '#title' => 'Item visible when multiple select trigger has value2', + '#states' => [ + 'visible' => [ + 'select[name="multiple_select_trigger[]"]' => ['value' => ['value2']], + ], + ], + ]; + $form['item_visible_when_multiple_select_trigger_has_no_value'] = [ + '#type' => 'item', + '#title' => 'Item visible when multiple select trigger has no value', + '#states' => [ + 'visible' => [ + 'select[name="multiple_select_trigger[]"]' => ['value' => []], + ], + ], + ]; + $form['textfield_visible_when_multiple_select_trigger_has_value3'] = [ + '#type' => 'textfield', + '#title' => 'Textfield visible when multiple select trigger has value3', + '#states' => [ + 'visible' => [ + 'select[name="multiple_select_trigger[]"]' => ['value' => ['value3']], + ], + ], + ]; + $form['textfield_visible_when_multiple_select_trigger_has_value2_or_value3'] = [ + '#type' => 'textfield', + '#title' => 'Textfield visible when multiple select trigger has value2 OR value3', + '#states' => [ + 'visible' => [ + 'select[name="multiple_select_trigger[]"]' => [ + ['value' => ['value2']], + ['value' => ['value3']], + ], + ], + ], + ]; + $form['textfield_visible_when_multiple_select_trigger_has_value2_and_value3'] = [ + '#type' => 'textfield', + '#title' => 'Textfield visible when multiple select trigger has value2 AND value3', + '#states' => [ + 'visible' => [ + 'select[name="multiple_select_trigger[]"]' => [ + 'value' => [ + 'value3', + 'value2', + ], + ], + ], + ], + ]; + // Textfield trigger. + $form['header_textfield'] = [ + '#type' => 'html_tag', + '#tag' => 'h3', + '#value' => 'Textfield tests', + ]; $form['checkbox_checked_when_textfield_trigger_filled'] = [ '#type' => 'checkbox', '#title' => 'Checkbox checked when textfield trigger filled', @@ -503,6 +611,11 @@ public function buildForm(array $form, FormStateInterface $form_state) { ]; // Multiple triggers. + $form['header_multiple_triggers'] = [ + '#type' => 'html_tag', + '#tag' => 'h3', + '#value' => 'Multiple triggers tests', + ]; $form['item_visible_when_select_trigger_has_value2_and_textfield_trigger_filled'] = [ '#type' => 'item', '#title' => 'Item visible when select trigger has value2 and textfield trigger filled', @@ -515,6 +628,11 @@ public function buildForm(array $form, FormStateInterface $form_state) { ]; // Number triggers. + $form['header_number'] = [ + '#type' => 'html_tag', + '#tag' => 'h3', + '#value' => 'Number tests', + ]; $form['item_visible_when_number_trigger_filled_by_spinner'] = [ '#type' => 'item', '#title' => 'Item visible when number trigger filled by spinner widget', diff --git a/core/tests/Drupal/FunctionalJavascriptTests/Core/Form/JavascriptStatesTest.php b/core/tests/Drupal/FunctionalJavascriptTests/Core/Form/JavascriptStatesTest.php index 83941319792e..b5c0d9178906 100644 --- a/core/tests/Drupal/FunctionalJavascriptTests/Core/Form/JavascriptStatesTest.php +++ b/core/tests/Drupal/FunctionalJavascriptTests/Core/Form/JavascriptStatesTest.php @@ -69,6 +69,7 @@ public function testJavascriptStates() { $this->doTextfieldTriggerTests(); $this->doRadiosTriggerTests(); $this->doSelectTriggerTests(); + $this->doMultipleSelectTriggerTests(); $this->doMultipleTriggerTests(); $this->doNestedTriggerTests(); $this->doElementsDisabledStateTests(); @@ -444,6 +445,63 @@ protected function doSelectTriggerTests() { $this->assertTrue($textfield_visible_value2_or_value3->isVisible()); } + /** + * Tests states of elements triggered by a multiple select element. + */ + protected function doMultipleSelectTriggerTests() { + $this->drupalGet('form-test/javascript-states-form'); + $page = $this->getSession()->getPage(); + // Find trigger and target elements. + $trigger = $page->findField('multiple_select_trigger[]'); + $this->assertNotEmpty($trigger); + $item_visible_value2 = $this->assertSession()->elementExists('css', '#edit-item-visible-when-multiple-select-trigger-has-value2'); + $item_visible_no_value = $this->assertSession()->elementExists('css', '#edit-item-visible-when-multiple-select-trigger-has-no-value'); + $textfield_visible_value3 = $page->findField('textfield_visible_when_multiple_select_trigger_has_value3'); + $this->assertNotEmpty($textfield_visible_value3); + $textfield_visible_value2_or_value3 = $page->findField('textfield_visible_when_multiple_select_trigger_has_value2_or_value3'); + $this->assertNotEmpty($textfield_visible_value2_or_value3); + $textfield_visible_value2_and_value3 = $page->findField('textfield_visible_when_multiple_select_trigger_has_value2_and_value3'); + $this->assertNotEmpty($textfield_visible_value2_and_value3); + + // Verify initial state. + $this->assertFalse($item_visible_value2->isVisible()); + $this->assertTrue($item_visible_no_value->isVisible()); + $this->assertFalse($textfield_visible_value3->isVisible()); + $this->assertFalse($textfield_visible_value2_or_value3->isVisible()); + $this->assertFalse($textfield_visible_value2_and_value3->isVisible()); + // Change state: select the 'Value 2' option. + $trigger->setValue('value2'); + $this->assertTrue($item_visible_value2->isVisible()); + $this->assertFalse($item_visible_no_value->isVisible()); + $this->assertFalse($textfield_visible_value3->isVisible()); + $this->assertTrue($textfield_visible_value2_or_value3->isVisible()); + $this->assertFalse($textfield_visible_value2_and_value3->isVisible()); + // Change state: select the 'Value 3' option. + $trigger->setValue('value3'); + $this->assertFalse($item_visible_value2->isVisible()); + $this->assertFalse($item_visible_no_value->isVisible()); + $this->assertTrue($textfield_visible_value3->isVisible()); + $this->assertTrue($textfield_visible_value2_or_value3->isVisible()); + $this->assertFalse($textfield_visible_value2_and_value3->isVisible()); + // Change state: select 'Value2' and 'Value 3' options. + $trigger->setValue(['value2', 'value3']); + $this->assertFalse($item_visible_value2->isVisible()); + $this->assertFalse($item_visible_no_value->isVisible()); + $this->assertFalse($textfield_visible_value3->isVisible()); + $this->assertFalse($textfield_visible_value2_or_value3->isVisible()); + $this->assertTrue($textfield_visible_value2_and_value3->isVisible()); + // Restore initial trigger state (clear the values). + $trigger->setValue([]); + // Make sure the initial element states are restored. + $this->assertFalse($item_visible_value2->isVisible()); + $this->assertFalse($textfield_visible_value3->isVisible()); + $this->assertFalse($textfield_visible_value2_or_value3->isVisible()); + // @todo These last two look to be correct, but the assertion is failing. + // @see https://www.drupal.org/project/drupal/issues/3367310 + // $this->assertTrue($item_visible_no_value->isVisible()); + // $this->assertFalse($textfield_visible_value2_and_value3->isVisible()); + } + /** * Tests states of elements triggered by multiple elements. */ -- GitLab