diff --git a/core/misc/states.js b/core/misc/states.js index a55c4d08db00e8b36c6d5d7b67029acab791448e..3109e1f1bdb0b02437c6c429cfdc3657e40dec53 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 bf367003d1050fb243b8149ca0ede1b708328ae8..0677df3bd06e3a2be32510d7208f670e6cba5ab7 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 83941319792e7c2f67b62757eb8678a5852f0a7e..b5c0d9178906e38fc19aa3f067d3c9d6ae4325f7 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. */