From fdb4b77574d336e0ca99f0b26fa24f381612c749 Mon Sep 17 00:00:00 2001
From: Maximo Mena <maximo.mena@lullabot.com>
Date: Thu, 20 Feb 2025 00:27:41 -0700
Subject: [PATCH 1/7] Only the summary's first child node should be set as the
 vertical tab title

---
 core/misc/vertical-tabs.js | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/core/misc/vertical-tabs.js b/core/misc/vertical-tabs.js
index 3b628bf2fba2..3a0833ed4cad 100644
--- a/core/misc/vertical-tabs.js
+++ b/core/misc/vertical-tabs.js
@@ -85,8 +85,11 @@
           $details.each(function () {
             const $that = $(this);
             const $summary = $that.find('> summary');
+            // We're only interested in the summary's first child node to prevent
+            // duplicate text in the title.
+            const title = $summary.length && $summary[0].childNodes.length ? $summary[0].firstChild.textContent : '';
             const verticalTab = new Drupal.verticalTab({
-              title: $summary.length ? $summary[0].textContent : '',
+              title: title,
               details: $that,
             });
             tabList.append(verticalTab.item);
-- 
GitLab


From 165acb4c3d442a915d6bb3749e593ea3dd742eac Mon Sep 17 00:00:00 2001
From: Maximo Mena <maximo.mena@lullabot.com>
Date: Thu, 20 Feb 2025 03:18:51 -0700
Subject: [PATCH 2/7] Fix lint issues

---
 core/misc/vertical-tabs.js | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/core/misc/vertical-tabs.js b/core/misc/vertical-tabs.js
index 3a0833ed4cad..6dfba606c1a1 100644
--- a/core/misc/vertical-tabs.js
+++ b/core/misc/vertical-tabs.js
@@ -87,9 +87,12 @@
             const $summary = $that.find('> summary');
             // We're only interested in the summary's first child node to prevent
             // duplicate text in the title.
-            const title = $summary.length && $summary[0].childNodes.length ? $summary[0].firstChild.textContent : '';
+            const title =
+              $summary.length && $summary[0].childNodes.length
+                ? $summary[0].firstChild.textContent
+                : '';
             const verticalTab = new Drupal.verticalTab({
-              title: title,
+              title,
               details: $that,
             });
             tabList.append(verticalTab.item);
-- 
GitLab


From 61fc0c131b84122a0bfb489155aab7225d0154ba Mon Sep 17 00:00:00 2001
From: Maximo Mena <maximo.mena@lullabot.com>
Date: Fri, 21 Feb 2025 14:33:28 -0700
Subject: [PATCH 3/7] Improve explanation of code changes in comment

---
 core/misc/vertical-tabs.js | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/core/misc/vertical-tabs.js b/core/misc/vertical-tabs.js
index 6dfba606c1a1..2584b2cdb867 100644
--- a/core/misc/vertical-tabs.js
+++ b/core/misc/vertical-tabs.js
@@ -85,8 +85,10 @@
           $details.each(function () {
             const $that = $(this);
             const $summary = $that.find('> summary');
-            // We're only interested in the summary's first child node to prevent
-            // duplicate text in the title.
+            // Summary elements often have 2 child nodes: a text title and a
+            // dynamic summary wrapped in <span>. To set the vertical tab title,
+            // we only want to copy the summary title, which is the first child
+            // node.
             const title =
               $summary.length && $summary[0].childNodes.length
                 ? $summary[0].firstChild.textContent
-- 
GitLab


From 5e8cb00ec536993d481280c820f3f713a59f6eea Mon Sep 17 00:00:00 2001
From: Maximo Mena <maximo.mena@lullabot.com>
Date: Sat, 22 Feb 2025 00:59:04 -0700
Subject: [PATCH 4/7] Add test coverage for vertical-tabs form elements with
 dynamic summaries

---
 .../modules/form_test/form_test.libraries.yml | 10 +++
 .../modules/form_test/form_test.routing.yml   |  8 ++
 .../tests/modules/form_test/js/form_test.js   | 28 +++++++
 .../FormTestVerticalTabsWithSummaryForm.php   | 73 +++++++++++++++++++
 .../ElementsVerticalTabsWithSummaryTest.php   | 37 ++++++++++
 5 files changed, 156 insertions(+)
 create mode 100644 core/modules/system/tests/modules/form_test/form_test.libraries.yml
 create mode 100644 core/modules/system/tests/modules/form_test/js/form_test.js
 create mode 100644 core/modules/system/tests/modules/form_test/src/Form/FormTestVerticalTabsWithSummaryForm.php
 create mode 100644 core/modules/system/tests/src/FunctionalJavascript/Form/ElementsVerticalTabsWithSummaryTest.php

diff --git a/core/modules/system/tests/modules/form_test/form_test.libraries.yml b/core/modules/system/tests/modules/form_test/form_test.libraries.yml
new file mode 100644
index 000000000000..9c0d8d756b41
--- /dev/null
+++ b/core/modules/system/tests/modules/form_test/form_test.libraries.yml
@@ -0,0 +1,10 @@
+form_test:
+  version: VERSION
+  js:
+    # Weight is set to a negative number to ensure that details summaries
+    # are loaded before vertical-tabs.js to simulate what happens on
+    # entity forms.
+    js/form_test.js: { weight: -1 }
+  dependencies:
+    - core/jquery
+    - core/drupal
diff --git a/core/modules/system/tests/modules/form_test/form_test.routing.yml b/core/modules/system/tests/modules/form_test/form_test.routing.yml
index 3aa58524b712..b4ead833a4d1 100644
--- a/core/modules/system/tests/modules/form_test/form_test.routing.yml
+++ b/core/modules/system/tests/modules/form_test/form_test.routing.yml
@@ -180,6 +180,14 @@ form_test.vertical_tabs:
   requirements:
     _access: 'TRUE'
 
+form_test.vertical_tabs_with_summary:
+  path: '/form_test/vertical-tabs-with-summary'
+  defaults:
+    _form: '\Drupal\form_test\Form\FormTestVerticalTabsWithSummaryForm'
+    _title: 'Vertical tabs with Summary tests'
+  requirements:
+    _access: 'TRUE'
+
 form_test.storage:
   path: '/form_test/form-storage'
   defaults:
diff --git a/core/modules/system/tests/modules/form_test/js/form_test.js b/core/modules/system/tests/modules/form_test/js/form_test.js
new file mode 100644
index 000000000000..08fab640b33f
--- /dev/null
+++ b/core/modules/system/tests/modules/form_test/js/form_test.js
@@ -0,0 +1,28 @@
+/**
+ * @file
+ * Attaches behaviors for the form_test module.
+ */
+(function ($, Drupal) {
+  /**
+   * Behavior for setting dynamic summaries.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches summary behavior on path edit forms.
+   */
+  Drupal.behaviors.formTestVerticalTabsSummary = {
+    attach(context) {
+      $(context)
+        .find('[data-drupal-selector="edit-tab1"]')
+        .drupalSetSummary((context) => {
+          return 'Summary 1';
+        });
+      $(context)
+        .find('[data-drupal-selector="edit-tab2"]')
+        .drupalSetSummary((context) => {
+          return 'Summary 2';
+        });
+    },
+  };
+})(jQuery, Drupal);
diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestVerticalTabsWithSummaryForm.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestVerticalTabsWithSummaryForm.php
new file mode 100644
index 000000000000..ae875ed40ab4
--- /dev/null
+++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestVerticalTabsWithSummaryForm.php
@@ -0,0 +1,73 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\form_test\Form;
+
+use Drupal\Component\Serialization\Json;
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * Builds a simple form to test vertical-tabs form element with tab summaries.
+ *
+ * @internal
+ */
+class FormTestVerticalTabsWithSummaryForm extends FormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return '_form_test_vertical_tabs_with_summary_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+
+    $form['information'] = [
+        '#type' => 'vertical_tabs',
+        '#default_tab' => 'edit-tab1',
+    ];
+    $form['tab1'] = [
+        '#type' => 'details',
+        '#title' => $this->t('Tab 1'),
+        '#group' => 'information',
+    ];
+    $form['tab1']['field1'] = [
+        '#type' => 'textfield',
+        '#title' => $this->t('Field 1'),
+    ];
+    $form['tab2'] = [
+        '#type' => 'details',
+        '#title' => $this->t('Tab 2'),
+        '#group' => 'information',
+    ];
+    $form['tab2']['field2'] = [
+        '#type' => 'textfield',
+        '#title' => $this->t('Field 2'),
+    ];
+
+    $form['actions']['#type'] = 'actions';
+    $form['actions']['submit'] = [
+      '#type' => 'submit',
+      '#value' => $this->t('Save'),
+      '#button_type' => 'primary',
+    ];
+
+    // Attach the library that sets the vertical tab summaries.
+    $form['#attached']['library'][] = 'form_test/form_test';
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $this->messenger()->addStatus($this->t('Form submitted.'));
+  }
+
+}
diff --git a/core/modules/system/tests/src/FunctionalJavascript/Form/ElementsVerticalTabsWithSummaryTest.php b/core/modules/system/tests/src/FunctionalJavascript/Form/ElementsVerticalTabsWithSummaryTest.php
new file mode 100644
index 000000000000..376a9f96170c
--- /dev/null
+++ b/core/modules/system/tests/src/FunctionalJavascript/Form/ElementsVerticalTabsWithSummaryTest.php
@@ -0,0 +1,37 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\Tests\system\FunctionalJavascript\Form;
+
+use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
+
+/**
+ * Tests that titles and summaries in vertical-tabs form elements are set correctly.
+ *
+ * @group Form
+ */
+class ElementsVerticalTabsWithSummaryTest extends WebDriverTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = ['form_test'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $defaultTheme = 'stark';
+
+  /**
+   * Check that vertical tabs title and summaries are set correctly.
+   */
+  public function testDynamicSummary(): void {
+    $this->drupalGet('form_test/vertical-tabs-with-summary');
+    $this->assertSession()->elementTextEquals('css', '.vertical-tabs__menu-item.first .vertical-tabs__menu-item-title', 'Tab 1');
+    $this->assertSession()->elementTextEquals('css', '.vertical-tabs__menu-item.first .vertical-tabs__menu-item-summary', 'Summary 1');
+    $this->assertSession()->elementTextEquals('css', '.vertical-tabs__menu-item.last .vertical-tabs__menu-item-title', 'Tab 2');
+    $this->assertSession()->elementTextEquals('css', '.vertical-tabs__menu-item.last .vertical-tabs__menu-item-summary', 'Summary 2');
+  }
+
+}
-- 
GitLab


From 7a6b4d04a44c2597fffbd3746b51036180cff731 Mon Sep 17 00:00:00 2001
From: Maximo Mena <maximo.mena@lullabot.com>
Date: Sat, 22 Feb 2025 01:11:35 -0700
Subject: [PATCH 5/7] Fix PHP coding standards issues

---
 .../FormTestVerticalTabsWithSummaryForm.php   | 25 +++++++++----------
 1 file changed, 12 insertions(+), 13 deletions(-)

diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestVerticalTabsWithSummaryForm.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestVerticalTabsWithSummaryForm.php
index ae875ed40ab4..799cc775e329 100644
--- a/core/modules/system/tests/modules/form_test/src/Form/FormTestVerticalTabsWithSummaryForm.php
+++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestVerticalTabsWithSummaryForm.php
@@ -4,7 +4,6 @@
 
 namespace Drupal\form_test\Form;
 
-use Drupal\Component\Serialization\Json;
 use Drupal\Core\Form\FormBase;
 use Drupal\Core\Form\FormStateInterface;
 
@@ -28,26 +27,26 @@ public function getFormId() {
   public function buildForm(array $form, FormStateInterface $form_state) {
 
     $form['information'] = [
-        '#type' => 'vertical_tabs',
-        '#default_tab' => 'edit-tab1',
+      '#type' => 'vertical_tabs',
+      '#default_tab' => 'edit-tab1',
     ];
     $form['tab1'] = [
-        '#type' => 'details',
-        '#title' => $this->t('Tab 1'),
-        '#group' => 'information',
+      '#type' => 'details',
+      '#title' => $this->t('Tab 1'),
+      '#group' => 'information',
     ];
     $form['tab1']['field1'] = [
-        '#type' => 'textfield',
-        '#title' => $this->t('Field 1'),
+      '#type' => 'textfield',
+      '#title' => $this->t('Field 1'),
     ];
     $form['tab2'] = [
-        '#type' => 'details',
-        '#title' => $this->t('Tab 2'),
-        '#group' => 'information',
+      '#type' => 'details',
+      '#title' => $this->t('Tab 2'),
+      '#group' => 'information',
     ];
     $form['tab2']['field2'] = [
-        '#type' => 'textfield',
-        '#title' => $this->t('Field 2'),
+      '#type' => 'textfield',
+      '#title' => $this->t('Field 2'),
     ];
 
     $form['actions']['#type'] = 'actions';
-- 
GitLab


From fd10df5ecb89f43be96df1b16d419dd58c47f21b Mon Sep 17 00:00:00 2001
From: Maximo Mena <maximo.mena@lullabot.com>
Date: Mon, 3 Mar 2025 05:26:25 -0700
Subject: [PATCH 6/7]  Fix PHPStan issues

---
 .../form_test/src/Form/FormTestVerticalTabsWithSummaryForm.php  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestVerticalTabsWithSummaryForm.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestVerticalTabsWithSummaryForm.php
index 799cc775e329..30080340fad6 100644
--- a/core/modules/system/tests/modules/form_test/src/Form/FormTestVerticalTabsWithSummaryForm.php
+++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestVerticalTabsWithSummaryForm.php
@@ -65,7 +65,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
   /**
    * {@inheritdoc}
    */
-  public function submitForm(array &$form, FormStateInterface $form_state) {
+  public function submitForm(array &$form, FormStateInterface $form_state): void {
     $this->messenger()->addStatus($this->t('Form submitted.'));
   }
 
-- 
GitLab


From bdd451738225f92b616324f5ab235945501fd436 Mon Sep 17 00:00:00 2001
From: Maximo Mena <maximo.mena@lullabot.com>
Date: Thu, 27 Mar 2025 06:50:20 -0700
Subject: [PATCH 7/7] Optimizing JS code changes

---
 core/misc/vertical-tabs.js | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/core/misc/vertical-tabs.js b/core/misc/vertical-tabs.js
index 2584b2cdb867..9d2e955fc828 100644
--- a/core/misc/vertical-tabs.js
+++ b/core/misc/vertical-tabs.js
@@ -89,10 +89,7 @@
             // dynamic summary wrapped in <span>. To set the vertical tab title,
             // we only want to copy the summary title, which is the first child
             // node.
-            const title =
-              $summary.length && $summary[0].childNodes.length
-                ? $summary[0].firstChild.textContent
-                : '';
+            const title = $summary[0]?.firstChild?.textContent || '';
             const verticalTab = new Drupal.verticalTab({
               title,
               details: $that,
-- 
GitLab