From 36b2bfae686ad27b6f0653ad52133a6f60560f02 Mon Sep 17 00:00:00 2001
From: Alex Pott <alex.a.pott@googlemail.com>
Date: Fri, 18 Oct 2024 18:37:17 +0100
Subject: [PATCH] Issue #1427838 by sukr_s, smustgrave, quietone, tim.plunkett,
 alexpott: password_confirm children do not pick up #states or #attributes

---
 core/lib/Drupal/Core/Form/FormHelper.php               | 10 +++++-----
 .../form_test/src/Form/JavascriptStatesForm.php        |  9 +++++++++
 .../Core/Form/JavascriptStatesTest.php                 |  6 ++++++
 core/tests/Drupal/Tests/Core/Form/FormHelperTest.php   |  2 ++
 4 files changed, 22 insertions(+), 5 deletions(-)

diff --git a/core/lib/Drupal/Core/Form/FormHelper.php b/core/lib/Drupal/Core/Form/FormHelper.php
index 8ac7266a1649..08e2035d2597 100644
--- a/core/lib/Drupal/Core/Form/FormHelper.php
+++ b/core/lib/Drupal/Core/Form/FormHelper.php
@@ -204,11 +204,11 @@ protected static function processStatesArray(array &$conditions, $search, $repla
    */
   public static function processStates(array &$elements) {
     $elements['#attached']['library'][] = 'core/drupal.states';
-    // Elements of '#type' => 'item' are not actual form input elements, but we
-    // still want to be able to show/hide them. Since there's no actual HTML
-    // input element available, setting #attributes does not make sense, but a
-    // wrapper is available, so setting #wrapper_attributes makes it work.
-    $key = ($elements['#type'] == 'item') ? '#wrapper_attributes' : '#attributes';
+    // Elements that are actual form input elements, use '#attributes'.
+    // In cases like 'item' that are not actual form input elements or
+    // those like 'password_confirm' that have child elements,
+    // use #wrapper_attributes.
+    $key = (($elements['#markup'] ?? FALSE) === '' && ($elements['#input'] ?? FALSE) === TRUE) ? '#wrapper_attributes' : '#attributes';
     $elements[$key]['data-drupal-states'] = Json::encode($elements['#states']);
   }
 
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 7a93c1d32e15..6de8e4ad1855 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
@@ -732,6 +732,15 @@ public function buildForm(array $form, FormStateInterface $form_state) {
       '#title' => 'Enable textarea',
     ];
 
+    $form['password_confirm'] = [
+      '#title' => $this->t('Enter password'),
+      '#type' => 'password_confirm',
+      '#states' => [
+        'visible' => [
+          ':input[name="checkbox_trigger"]' => ['checked' => FALSE],
+        ],
+      ],
+    ];
     $form['test_select_visible_dependence']['select_visible_1'] = [
       '#type' => 'select',
       '#title' => 'Select visible 1',
diff --git a/core/tests/Drupal/FunctionalJavascriptTests/Core/Form/JavascriptStatesTest.php b/core/tests/Drupal/FunctionalJavascriptTests/Core/Form/JavascriptStatesTest.php
index 179886e8b900..50dc03c150d1 100644
--- a/core/tests/Drupal/FunctionalJavascriptTests/Core/Form/JavascriptStatesTest.php
+++ b/core/tests/Drupal/FunctionalJavascriptTests/Core/Form/JavascriptStatesTest.php
@@ -187,6 +187,8 @@ protected function doCheckboxTriggerTests() {
     $this->assertFalse($radios_some_disabled_value2->hasAttribute('disabled'));
     // Check if the link is visible.
     $this->assertTrue($link->isVisible());
+    // Check enter password is visible.
+    $this->assertSession()->pageTextContains('Enter password');
 
     // Change state: check the checkbox.
     $trigger->check();
@@ -228,6 +230,8 @@ protected function doCheckboxTriggerTests() {
     $this->assertFalse($radios_some_disabled_value2->hasAttribute('disabled'));
     // The link shouldn't be visible.
     $this->assertFalse($link->isVisible());
+    // Check enter password is not visible.
+    $this->assertSession()->pageTextNotContains('Enter password');
 
     // Change state: uncheck the checkbox.
     $trigger->uncheck();
@@ -263,6 +267,8 @@ protected function doCheckboxTriggerTests() {
     $this->assertFalse($radios_some_disabled_value2->hasAttribute('disabled'));
     // Check if the link is turned back to visible state.
     $this->assertTrue($link->isVisible());
+    // Check enter password is visible.
+    $this->assertSession()->pageTextContains('Enter password');
   }
 
   /**
diff --git a/core/tests/Drupal/Tests/Core/Form/FormHelperTest.php b/core/tests/Drupal/Tests/Core/Form/FormHelperTest.php
index 2da73b861e10..7c1e6beca2ea 100644
--- a/core/tests/Drupal/Tests/Core/Form/FormHelperTest.php
+++ b/core/tests/Drupal/Tests/Core/Form/FormHelperTest.php
@@ -120,6 +120,8 @@ public static function providerElements() {
               ':input[name="foo"]' => ['value' => 'bar'],
             ],
           ],
+          '#markup' => '',
+          '#input' => TRUE,
         ],
         '#wrapper_attributes',
       ],
-- 
GitLab