From 0455e2be7f481b3a26a2b95b9221aab4a861df8c Mon Sep 17 00:00:00 2001
From: Brian Canini <canini.16@osu.edu>
Date: Mon, 29 Jun 2020 14:25:21 -0400
Subject: [PATCH] Updating drupal/field_group (3.0.0 => 3.1.0)

---
 composer.json                                 |   2 +-
 composer.lock                                 |  29 +-
 vendor/composer/installed.json                |  29 +-
 web/modules/field_group/composer.json         |  11 +-
 .../field_group_migrate.info.yml              |   8 +-
 .../migrate/destination/d7/FieldGroup.php     |   4 +-
 web/modules/field_group/field_group.info.yml  |   9 +-
 web/modules/field_group/field_group.install   |  50 +++
 web/modules/field_group/field_group.module    |  94 ++----
 .../formatters/tabs/horizontal-tabs.css       |   1 +
 .../formatters/tabs/horizontal-tabs.js        |  10 +-
 web/modules/field_group/includes/field_ui.inc |   1 -
 .../field_group/src/Element/VerticalTabs.php  |   3 +-
 .../src/FieldGroupFormatterBase.php           |   6 +-
 .../src/Form/FieldGroupAddForm.php            |   4 +-
 .../src/Form/FieldGroupDeleteForm.php         |   4 +-
 .../field_group/src/FormatterHelper.php       |  96 +++++-
 .../field_group_test.info.yml                 |   8 +-
 .../src/Functional/EntityDisplayTest.php      |  40 +--
 .../src/Functional/FieldGroupTestTrait.php    |   8 +-
 .../HorizontalTabsLabelsTest.php              | 288 ++++++++++++++++++
 21 files changed, 550 insertions(+), 155 deletions(-)
 create mode 100644 web/modules/field_group/tests/src/FunctionalJavascript/HorizontalTabsLabelsTest.php

diff --git a/composer.json b/composer.json
index aabb08e27f..76d7240f55 100644
--- a/composer.json
+++ b/composer.json
@@ -120,7 +120,7 @@
         "drupal/entity_reference_revisions": "1.8",
         "drupal/externalauth": "1.1",
         "drupal/features": "3.8",
-        "drupal/field_group": "3.0",
+        "drupal/field_group": "3.1",
         "drupal/field_permissions": "1.0",
         "drupal/file_browser": "1.1",
         "drupal/focal_point": "1.4",
diff --git a/composer.lock b/composer.lock
index cd907bc32a..6d90d94e17 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "7d3787aaafd1ba3db8d0785fee363f8b",
+    "content-hash": "2efebe1e5a975379bafab0314e809f18",
     "packages": [
         {
             "name": "alchemy/zippy",
@@ -4813,29 +4813,29 @@
         },
         {
             "name": "drupal/field_group",
-            "version": "3.0.0",
+            "version": "3.1.0",
             "source": {
                 "type": "git",
                 "url": "https://git.drupalcode.org/project/field_group.git",
-                "reference": "8.x-3.0"
+                "reference": "8.x-3.1"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://ftp.drupal.org/files/projects/field_group-8.x-3.0.zip",
-                "reference": "8.x-3.0",
-                "shasum": "8d87cdc4abc417aa4d411bffcaeb0a3ef1afa497"
+                "url": "https://ftp.drupal.org/files/projects/field_group-8.x-3.1.zip",
+                "reference": "8.x-3.1",
+                "shasum": "8a719eaea594f0ba874172831cb28da93c66b77a"
             },
             "require": {
-                "drupal/core": "^8 || ^9"
+                "drupal/core": "^8.8 || ^9"
+            },
+            "require-dev": {
+                "drupal/jquery_ui_accordion": "^1.0"
             },
             "type": "drupal-module",
             "extra": {
-                "branch-alias": {
-                    "dev-3.x": "3.x-dev"
-                },
                 "drupal": {
-                    "version": "8.x-3.0",
-                    "datestamp": "1580250787",
+                    "version": "8.x-3.1",
+                    "datestamp": "1591772567",
                     "security-coverage": {
                         "status": "covered",
                         "message": "Covered by Drupal's security advisory policy"
@@ -4844,7 +4844,7 @@
             },
             "notification-url": "https://packages.drupal.org/8/downloads",
             "license": [
-                "GPL-2.0+"
+                "GPL-2.0-or-later"
             ],
             "authors": [
                 {
@@ -4871,7 +4871,8 @@
             "description": "Provides the field_group module.",
             "homepage": "https://www.drupal.org/project/field_group",
             "support": {
-                "source": "https://git.drupalcode.org/project/field_group"
+                "source": "https://git.drupalcode.org/project/field_group",
+                "issues": "https://www.drupal.org/project/issues/field_group"
             }
         },
         {
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
index 9d55d91d2d..6c0478828a 100644
--- a/vendor/composer/installed.json
+++ b/vendor/composer/installed.json
@@ -4955,30 +4955,30 @@
     },
     {
         "name": "drupal/field_group",
-        "version": "3.0.0",
-        "version_normalized": "3.0.0.0",
+        "version": "3.1.0",
+        "version_normalized": "3.1.0.0",
         "source": {
             "type": "git",
             "url": "https://git.drupalcode.org/project/field_group.git",
-            "reference": "8.x-3.0"
+            "reference": "8.x-3.1"
         },
         "dist": {
             "type": "zip",
-            "url": "https://ftp.drupal.org/files/projects/field_group-8.x-3.0.zip",
-            "reference": "8.x-3.0",
-            "shasum": "8d87cdc4abc417aa4d411bffcaeb0a3ef1afa497"
+            "url": "https://ftp.drupal.org/files/projects/field_group-8.x-3.1.zip",
+            "reference": "8.x-3.1",
+            "shasum": "8a719eaea594f0ba874172831cb28da93c66b77a"
         },
         "require": {
-            "drupal/core": "^8 || ^9"
+            "drupal/core": "^8.8 || ^9"
+        },
+        "require-dev": {
+            "drupal/jquery_ui_accordion": "^1.0"
         },
         "type": "drupal-module",
         "extra": {
-            "branch-alias": {
-                "dev-3.x": "3.x-dev"
-            },
             "drupal": {
-                "version": "8.x-3.0",
-                "datestamp": "1580250787",
+                "version": "8.x-3.1",
+                "datestamp": "1591772567",
                 "security-coverage": {
                     "status": "covered",
                     "message": "Covered by Drupal's security advisory policy"
@@ -4988,7 +4988,7 @@
         "installation-source": "dist",
         "notification-url": "https://packages.drupal.org/8/downloads",
         "license": [
-            "GPL-2.0+"
+            "GPL-2.0-or-later"
         ],
         "authors": [
             {
@@ -5015,7 +5015,8 @@
         "description": "Provides the field_group module.",
         "homepage": "https://www.drupal.org/project/field_group",
         "support": {
-            "source": "https://git.drupalcode.org/project/field_group"
+            "source": "https://git.drupalcode.org/project/field_group",
+            "issues": "https://www.drupal.org/project/issues/field_group"
         }
     },
     {
diff --git a/web/modules/field_group/composer.json b/web/modules/field_group/composer.json
index ac893ea517..47b16e766f 100644
--- a/web/modules/field_group/composer.json
+++ b/web/modules/field_group/composer.json
@@ -2,9 +2,16 @@
     "name": "drupal/field_group",
     "description": "Provides the field_group module.",
     "type": "drupal-module",
-    "license": "GPL-2.0+",
+    "license": "GPL-2.0-or-later",
     "minimum-stability": "dev",
     "require": {
-        "drupal/core": "^8 || ^9"
+        "drupal/core": "^8.8 || ^9"
+    },
+    "require-dev": {
+        "drupal/jquery_ui_accordion": "^1.0"
+    },
+    "support": {
+        "issues": "https://www.drupal.org/project/issues/field_group",
+        "source": "https://git.drupalcode.org/project/field_group"
     }
 }
diff --git a/web/modules/field_group/contrib/field_group_migrate/field_group_migrate.info.yml b/web/modules/field_group/contrib/field_group_migrate/field_group_migrate.info.yml
index f1a0fb93f4..de95c532dd 100644
--- a/web/modules/field_group/contrib/field_group_migrate/field_group_migrate.info.yml
+++ b/web/modules/field_group/contrib/field_group_migrate/field_group_migrate.info.yml
@@ -2,11 +2,11 @@ name: 'Field Group Migrate'
 type: module
 description: 'Provides the ability to migrate field groups from D6/D7 to D8.'
 package: Migration
-core: 8.x
+core_version_requirement: ^8.8 || ^9
 dependencies:
   - field_group:field_group
 
-# Information added by Drupal.org packaging script on 2020-01-28
-version: '8.x-3.0'
+# Information added by Drupal.org packaging script on 2020-06-10
+version: '8.x-3.1'
 project: 'field_group'
-datestamp: 1580250789
+datestamp: 1591772570
diff --git a/web/modules/field_group/contrib/field_group_migrate/src/Plugin/migrate/destination/d7/FieldGroup.php b/web/modules/field_group/contrib/field_group_migrate/src/Plugin/migrate/destination/d7/FieldGroup.php
index 828715ffd8..64b6c4cd17 100644
--- a/web/modules/field_group/contrib/field_group_migrate/src/Plugin/migrate/destination/d7/FieldGroup.php
+++ b/web/modules/field_group/contrib/field_group_migrate/src/Plugin/migrate/destination/d7/FieldGroup.php
@@ -88,8 +88,8 @@ public function fields(MigrationInterface $migration = NULL) {
    *   The entity display object.
    */
   protected function getEntity($entity_type, $bundle, $mode, $type) {
-    $function = $type == 'entity_form_display' ? 'entity_get_form_display' : 'entity_get_display';
-    return $function($entity_type, $bundle, $mode);
+    $function = $type == 'entity_form_display' ? 'getFormDisplay' : 'getViewDisplay';
+    return \Drupal::service('entity_display.repository')->$function($entity_type, $bundle, $mode);
   }
 
 }
diff --git a/web/modules/field_group/field_group.info.yml b/web/modules/field_group/field_group.info.yml
index 42618f0d09..0e4b01a222 100644
--- a/web/modules/field_group/field_group.info.yml
+++ b/web/modules/field_group/field_group.info.yml
@@ -2,12 +2,11 @@ name: 'Field Group'
 type: module
 description: 'Provides the ability to group your fields on both form and display.'
 package : Fields
-core: 8.x
-core_version_requirement: ^8 || ^9
+core_version_requirement: ^8.8 || ^9
 dependencies:
   - drupal:field
 
-# Information added by Drupal.org packaging script on 2020-01-28
-version: '8.x-3.0'
+# Information added by Drupal.org packaging script on 2020-06-10
+version: '8.x-3.1'
 project: 'field_group'
-datestamp: 1580250789
+datestamp: 1591772570
diff --git a/web/modules/field_group/field_group.install b/web/modules/field_group/field_group.install
index 659cf67a2f..b247a47a05 100644
--- a/web/modules/field_group/field_group.install
+++ b/web/modules/field_group/field_group.install
@@ -5,9 +5,59 @@
  * Update hooks for the Field Group module.
  */
 
+/**
+ * Implements hook_requirements().
+ */
+function field_group_requirements($phase) {
+  $requirements = [];
+
+  if ($phase == 'runtime') {
+    // Check jQuery UI Accordion module for D9.
+    if (version_compare(\Drupal::VERSION, 9) > 0) {
+      if (!\Drupal::moduleHandler()->moduleExists('jquery_ui_accordion')) {
+        $requirements['field_group_jquery_ui_accordion'] = [
+          'title' => t('Field Group'),
+          'value' => t('jQuery UI Accordion not enabled'),
+          'description' => t('If you want to use the Field Group accordion formatter, you will need to install the <a href=":link" target="_blank">jQuery UI Accordion</a> module.', [':link' => 'https://www.drupal.org/project/jquery_ui_accordion']),
+          'severity' => REQUIREMENT_WARNING,
+        ];
+      }
+      else {
+        $requirements['field_group_jquery_ui_accordion'] = [
+          'title' => t('Field Group'),
+          'description' => t('The jQuery UI Accordion module is installed'),
+          'severity' => REQUIREMENT_INFO,
+        ];
+      }
+    }
+  }
+
+  return $requirements;
+}
+
 /**
  * Removed in favor of hook_post_update script.
  */
 function field_group_update_8301() {
   // @see field_group_post_update_0001().
 }
+
+/**
+ * Install the 'jquery_ui_accordion' module if it exists.
+ */
+function field_group_update_8302() {
+  try {
+    // Enables the jQuery UI accordion module if it exists.
+    if (\Drupal::service('extension.list.module')
+      ->getName('jquery_ui_accordion')) {
+      \Drupal::service('module_installer')
+        ->install(['jquery_ui_accordion'], FALSE);
+      return t('The "jquery_ui_accordion" module has been installed.');
+    }
+  }
+  catch (\Exception $e) {
+    return
+      t('If you want to use the Field Group accordion formatter, you will need to install the <a href=":link" target="_blank">jQuery UI Accordion</a> module.',
+        [':link' => 'https://www.drupal.org/project/jquery_ui_accordion']);
+  }
+}
diff --git a/web/modules/field_group/field_group.module b/web/modules/field_group/field_group.module
index 2e794568cf..006ae6f6e3 100644
--- a/web/modules/field_group/field_group.module
+++ b/web/modules/field_group/field_group.module
@@ -16,6 +16,7 @@
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Render\Element;
 use Drupal\field_group\Element\VerticalTabs;
+use Drupal\field_group\FormatterHelper;
 
 /**
  * Implements hook_help().
@@ -32,6 +33,24 @@ function field_group_help($route_name, RouteMatchInterface $route_match) {
   }
 }
 
+/**
+ * Implements hook_library_info_alter().
+ */
+function field_group_library_info_alter(&$libraries, $extension) {
+  // Swap jQuery.ui library if available.
+  // See https://www.drupal.org/project/field_group/issues/3109552 for more
+  // background on the logic.
+  if (version_compare(\Drupal::VERSION, 9) > 0 && $extension == 'field_group') {
+    if (\Drupal::moduleHandler()->moduleExists('jquery_ui_accordion')) {
+      $libraries['formatter.accordion']['dependencies'] = ['jquery_ui_accordion/accordion'];
+    }
+    else {
+      $libraries['formatter.accordion']['js'] = [];
+      $libraries['formatter.accordion']['dependencies'] = [];
+    }
+  }
+}
+
 /**
  * Implements hook_theme_registry_alter().
  */
@@ -235,7 +254,7 @@ function field_group_form_alter(array &$form, FormStateInterface $form_state) {
       ];
 
       field_group_attach_groups($form, $context);
-      $form['#process'][] = 'field_group_form_process';
+      $form['#process'][] = [FormatterHelper::class, 'formProcess'];
     }
   }
 
@@ -256,7 +275,7 @@ function field_group_inline_entity_form_entity_form_alter(&$entity_form, FormSta
   ];
 
   field_group_attach_groups($entity_form, $context);
-  field_group_form_process($entity_form);
+  FormatterHelper::formProcess($entity_form, $form_state);
 }
 
 /**
@@ -277,7 +296,7 @@ function field_group_form_layout_builder_update_block_alter(&$form, FormStateInt
   ];
 
   field_group_attach_groups($form['settings']['block_form'], $context);
-  $form['settings']['block_form']['#process'][] = 'field_group_form_process';
+  $form['settings']['block_form']['#process'][] = [FormatterHelper::class, 'formProcess'];
 }
 
 /**
@@ -317,7 +336,7 @@ function field_group_entity_view_alter(&$build, EntityInterface $entity, EntityD
 
     // If DS is enabled, no pre render is needed (DS adds fieldgroup preprocessing).
     if (!$ds_enabled) {
-      $build['#pre_render'][] = 'field_group_entity_view_pre_render';
+      $build['#pre_render'][] = [FormatterHelper::class, 'entityViewPrender'];
     }
   }
 }
@@ -349,72 +368,7 @@ function field_group_form_pre_render(array $element) {
  * @return array
  */
 function field_group_form_process(array &$element, FormStateInterface $form_state = NULL, array &$form = []) {
-  if (empty($element['#field_group_form_process'])) {
-    $element['#field_group_form_process'] = TRUE;
-    if (empty($element['#fieldgroups'])) {
-      return $element;
-    }
-
-    // Create all groups and keep a flat list of references to these groups.
-    $group_references = [];
-    foreach ($element['#fieldgroups'] as $group_name => $group) {
-      if (!isset($element[$group_name])) {
-        $element[$group_name] = [];
-      }
-
-      $group_parents = $element['#array_parents'];
-      $group_parents[] = empty($group->parent_name) ? $group->region : $group->parent_name;
-      $group_references[$group_name] = &$element[$group_name];
-      $element[$group_name]['#group'] = implode('][', $group_parents);
-
-      // Use array parents to set the group name. This will cover multilevel forms (eg paragraphs).
-      $parents = $element['#array_parents'];
-      $parents[] = $group_name;
-      $element[$group_name]['#parents'] = $parents;
-      $group_children_parent_group = implode('][', $parents);
-      foreach ($group->children as $child) {
-        if (!empty($element[$child]['#field_group_ignore'])) {
-          continue;
-        }
-        $element[$child]['#group'] = $group_children_parent_group;
-      }
-    }
-
-    foreach ($element['#fieldgroups'] as $group_name => $group) {
-      $field_group_element = &$element[$group_name];
-
-      // Let modules define their wrapping element.
-      // Note that the group element has no properties, only elements.
-      foreach (Drupal::moduleHandler()->getImplementations('field_group_form_process') as $module) {
-        // The intention here is to have the opportunity to alter the
-        // elements, as defined in hook_field_group_formatter_info.
-        // Note, implement $element by reference!
-        $function = $module . '_field_group_form_process';
-        $function($field_group_element, $group, $element);
-      }
-
-      // Allow others to alter the pre_render.
-      Drupal::moduleHandler()->alter('field_group_form_process', $field_group_element, $group, $element);
-    }
-
-    // Allow others to alter the complete processed build.
-    Drupal::moduleHandler()->alter('field_group_form_process_build', $element, $form_state, $form);
-  }
-
-  return $element;
-}
-
-/**
- * Pre render callback for rendering groups on entities without theme hook.
- *
- * @param array $element
- *   Entity being rendered.
- *
- * @return array
- */
-function field_group_entity_view_pre_render(array $element) {
-  field_group_build_entity_groups($element, 'view');
-  return $element;
+  return FormatterHelper::formProcess($element, $form_state, $form);
 }
 
 /**
diff --git a/web/modules/field_group/formatters/tabs/horizontal-tabs.css b/web/modules/field_group/formatters/tabs/horizontal-tabs.css
index b28d5c9dea..5aa784f928 100644
--- a/web/modules/field_group/formatters/tabs/horizontal-tabs.css
+++ b/web/modules/field_group/formatters/tabs/horizontal-tabs.css
@@ -31,6 +31,7 @@
   padding: 0 1em;
   border: 0;
   background-color: unset;
+  box-shadow: unset;
 }
 
 .horizontal-tabs-pane > summary {
diff --git a/web/modules/field_group/formatters/tabs/horizontal-tabs.js b/web/modules/field_group/formatters/tabs/horizontal-tabs.js
index ca0dc04ca5..865da55329 100644
--- a/web/modules/field_group/formatters/tabs/horizontal-tabs.js
+++ b/web/modules/field_group/formatters/tabs/horizontal-tabs.js
@@ -1,4 +1,4 @@
-(function ($) {
+(function ($, Drupal) {
 
   'use strict';
 
@@ -56,9 +56,9 @@
             summaryElement = $this.find('> summary');
           }
 
-          var summary = summaryElement.clone().children().remove().end().text();
+          var summaryText = summaryElement.clone().children().remove().end().text().trim() || summaryElement.find('> span:first-child').text().trim();
           var horizontal_tab = new Drupal.horizontalTab({
-            title: $.trim(summary),
+            title: summaryText,
             details: $this
           });
           horizontal_tab.item.addClass('horizontal-tab-button-' + i);
@@ -178,7 +178,6 @@
       // Display the tab.
       this.item.removeClass('horizontal-tab-hidden');
       this.item.show();
-      alert('show');
 
       // Update .first marker for items. We need recurse from parent to retain the
       // actual DOM element order as jQuery implements sortOrder, but not as public
@@ -201,7 +200,6 @@
       // Hide this tab.
       this.item.addClass('horizontal-tab-hidden');
       this.item.hide();
-      alert('hide');
 
       // Update .first marker for items. We need recurse from parent to retain the
       // actual DOM element order as jQuery implements sortOrder, but not as public
@@ -253,4 +251,4 @@
     return tab;
   };
 
-})(jQuery, Modernizr);
+})(jQuery, Drupal);
diff --git a/web/modules/field_group/includes/field_ui.inc b/web/modules/field_group/includes/field_ui.inc
index 0a14e6b771..5c53ff1a74 100644
--- a/web/modules/field_group/includes/field_ui.inc
+++ b/web/modules/field_group/includes/field_ui.inc
@@ -167,7 +167,6 @@ function field_group_field_ui_display_form_alter(&$form, FormStateInterface $for
     $settings = field_group_format_settings_form($group, $form, $form_state);
 
     $id = strtr($name, '_', '-');
-    $js_rows_data[$id] = ['type' => 'group', 'name' => $name];
     // A group cannot be selected as its own parent.
     $parent_options = $table['#parent_options'];
     $region = isset($group->region) && in_array($group->region, $params->available_regions) ? $group->region : $params->default_region;
diff --git a/web/modules/field_group/src/Element/VerticalTabs.php b/web/modules/field_group/src/Element/VerticalTabs.php
index 598e24cf47..c8a212d209 100644
--- a/web/modules/field_group/src/Element/VerticalTabs.php
+++ b/web/modules/field_group/src/Element/VerticalTabs.php
@@ -4,11 +4,12 @@
 
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Render\Element;
+use Drupal\Core\Render\Element\RenderCallbackInterface;
 
 /**
  * Provides extra processing and pre rendering on the vertical tabs.
  */
-class VerticalTabs {
+class VerticalTabs implements RenderCallbackInterface {
 
   /**
    * Pre render the group to support #group parameter.
diff --git a/web/modules/field_group/src/FieldGroupFormatterBase.php b/web/modules/field_group/src/FieldGroupFormatterBase.php
index 0230b9ac3b..b3297a637a 100644
--- a/web/modules/field_group/src/FieldGroupFormatterBase.php
+++ b/web/modules/field_group/src/FieldGroupFormatterBase.php
@@ -90,13 +90,13 @@ public function settingsForm() {
     $form = [];
     $form['label'] = [
       '#type' => 'textfield',
-      '#title' => t('Field group label'),
+      '#title' => $this->t('Field group label'),
       '#default_value' => $this->label,
       '#weight' => -5,
     ];
 
     $form['id'] = [
-      '#title' => t('ID'),
+      '#title' => $this->t('ID'),
       '#type' => 'textfield',
       '#default_value' => $this->getSetting('id'),
       '#weight' => 10,
@@ -104,7 +104,7 @@ public function settingsForm() {
     ];
 
     $form['classes'] = [
-      '#title' => t('Extra CSS classes'),
+      '#title' => $this->t('Extra CSS classes'),
       '#type' => 'textfield',
       '#default_value' => $this->getSetting('classes'),
       '#weight' => 11,
diff --git a/web/modules/field_group/src/Form/FieldGroupAddForm.php b/web/modules/field_group/src/Form/FieldGroupAddForm.php
index a69b5904be..7b89fb3aba 100644
--- a/web/modules/field_group/src/Form/FieldGroupAddForm.php
+++ b/web/modules/field_group/src/Form/FieldGroupAddForm.php
@@ -212,7 +212,7 @@ public function buildConfigurationForm(array &$form, FormStateInterface $form_st
     $group->bundle = $this->bundle;
     $group->mode = $this->mode;
 
-    $manager = \Drupal::service('plugin.manager.field_group.formatters');
+    $manager = $this->fieldGroupFormatterPluginManager;
     $plugin = $manager->getInstance([
       'format_type' => $form_state->getValue('group_formatter'),
       'configuration' => [
@@ -285,7 +285,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
       // Store new group information for any additional submit handlers.
       $groups_added = $form_state->get('groups_added');
       $groups_added['_add_new_group'] = $new_group->group_name;
-      $this->messenger->addMessage(t('New group %label successfully created.', ['%label' => $new_group->label]));
+      $this->messenger->addMessage($this->t('New group %label successfully created.', ['%label' => $new_group->label]));
 
       $form_state->setRedirectUrl(FieldgroupUi::getFieldUiRoute($new_group));
       \Drupal::cache()->invalidate('field_groups');
diff --git a/web/modules/field_group/src/Form/FieldGroupDeleteForm.php b/web/modules/field_group/src/Form/FieldGroupDeleteForm.php
index fafb76f1f1..6814525f96 100644
--- a/web/modules/field_group/src/Form/FieldGroupDeleteForm.php
+++ b/web/modules/field_group/src/Form/FieldGroupDeleteForm.php
@@ -82,7 +82,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
 
     field_group_delete_field_group($this->fieldGroup);
 
-    $this->messenger->addMessage(t('The group %group has been deleted from the %type content type.', ['%group' => t($this->fieldGroup->label), '%type' => $bundle_label]));
+    $this->messenger->addMessage($this->t('The group %group has been deleted from the %type content type.', ['%group' => $this->fieldGroup->label, '%type' => $bundle_label]));
 
     // Redirect.
     $form_state->setRedirectUrl($this->getCancelUrl());
@@ -93,7 +93,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
    * {@inheritdoc}
    */
   public function getQuestion() {
-    return $this->t('Are you sure you want to delete the group %group?', ['%group' => t($this->fieldGroup->label)]);
+    return $this->t('Are you sure you want to delete the group %group?', ['%group' => $this->fieldGroup->label]);
   }
 
   /**
diff --git a/web/modules/field_group/src/FormatterHelper.php b/web/modules/field_group/src/FormatterHelper.php
index 436d7d71ca..4b84072158 100644
--- a/web/modules/field_group/src/FormatterHelper.php
+++ b/web/modules/field_group/src/FormatterHelper.php
@@ -3,11 +3,13 @@
 namespace Drupal\field_group;
 
 use Drupal;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Security\TrustedCallbackInterface;
 
 /**
  * Static methods for fieldgroup formatters.
  */
-class FormatterHelper {
+class FormatterHelper implements TrustedCallbackInterface {
 
   /**
    * Return an array of field_group_formatter options.
@@ -18,7 +20,7 @@ public static function formatterOptions($type) {
     if (!isset($options)) {
       $options = [];
 
-      $manager = Drupal::service('plugin.manager.field_group.formatters');
+      $manager = \Drupal::service('plugin.manager.field_group.formatters');
       $formatters = $manager->getDefinitions();
 
       foreach ($formatters as $formatter) {
@@ -31,4 +33,94 @@ public static function formatterOptions($type) {
     return $options;
   }
 
+  /**
+   * Pre render callback for rendering groups on entities without theme hook.
+   *
+   * @param array $element
+   *   Entity being rendered.
+   *
+   * @return array
+   */
+  public static function entityViewPrender(array $element) {
+    field_group_build_entity_groups($element, 'view');
+    return $element;
+  }
+
+  /**
+   * Process callback for field groups.
+   *
+   * @param array $element
+   *   Form that is being processed.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   * @param array $form
+   *   The complete form structure.
+   *
+   * @return array
+   */
+  public static function formProcess(array &$element, FormStateInterface $form_state = NULL, array &$form = []) {
+    if (empty($element['#field_group_form_process'])) {
+      $element['#field_group_form_process'] = TRUE;
+      if (empty($element['#fieldgroups'])) {
+        return $element;
+      }
+
+      // Create all groups and keep a flat list of references to these groups.
+      $group_references = [];
+      foreach ($element['#fieldgroups'] as $group_name => $group) {
+        if (!isset($element[$group_name])) {
+          $element[$group_name] = [];
+        }
+
+        $group_parents = $element['#array_parents'];
+        $group_parents[] = empty($group->parent_name) ? $group->region : $group->parent_name;
+        $group_references[$group_name] = &$element[$group_name];
+        $element[$group_name]['#group'] = implode('][', $group_parents);
+
+        // Use array parents to set the group name. This will cover multilevel forms (eg paragraphs).
+        $parents = $element['#array_parents'];
+        $parents[] = $group_name;
+        $element[$group_name]['#parents'] = $parents;
+        $group_children_parent_group = implode('][', $parents);
+        foreach ($group->children as $child) {
+          if (!empty($element[$child]['#field_group_ignore'])) {
+            continue;
+          }
+          $element[$child]['#group'] = $group_children_parent_group;
+        }
+      }
+
+      foreach ($element['#fieldgroups'] as $group_name => $group) {
+        $field_group_element = &$element[$group_name];
+
+        // Let modules define their wrapping element.
+        // Note that the group element has no properties, only elements.
+        foreach (Drupal::moduleHandler()->getImplementations('field_group_form_process') as $module) {
+          // The intention here is to have the opportunity to alter the
+          // elements, as defined in hook_field_group_formatter_info.
+          // Note, implement $element by reference!
+          $function = $module . '_field_group_form_process';
+          $function($field_group_element, $group, $element);
+        }
+
+        // Allow others to alter the pre_render.
+        Drupal::moduleHandler()->alter('field_group_form_process', $field_group_element, $group, $element);
+      }
+
+      // Allow others to alter the complete processed build.
+      Drupal::moduleHandler()->alter('field_group_form_process_build', $element, $form_state, $form);
+    }
+
+    return $element;
+  }
+
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function trustedCallbacks() {
+    return ['entityViewPrender', 'formProcess'];
+  }
+
+
 }
diff --git a/web/modules/field_group/tests/modules/field_group_test/field_group_test.info.yml b/web/modules/field_group/tests/modules/field_group_test/field_group_test.info.yml
index 279040ed8e..b5e79b5971 100644
--- a/web/modules/field_group/tests/modules/field_group_test/field_group_test.info.yml
+++ b/web/modules/field_group/tests/modules/field_group_test/field_group_test.info.yml
@@ -1,11 +1,11 @@
 name: 'Field Group Test'
 description: 'Test module for Field Group'
-core: 8.x
+core_version_requirement: ^8.8 || ^9
 package: 'Fields'
 type: module
 hidden: TRUE
 
-# Information added by Drupal.org packaging script on 2020-01-28
-version: '8.x-3.0'
+# Information added by Drupal.org packaging script on 2020-06-10
+version: '8.x-3.1'
 project: 'field_group'
-datestamp: 1580250789
+datestamp: 1591772570
diff --git a/web/modules/field_group/tests/src/Functional/EntityDisplayTest.php b/web/modules/field_group/tests/src/Functional/EntityDisplayTest.php
index 7eda8fa638..5f0060ab37 100644
--- a/web/modules/field_group/tests/src/Functional/EntityDisplayTest.php
+++ b/web/modules/field_group/tests/src/Functional/EntityDisplayTest.php
@@ -23,7 +23,7 @@ class EntityDisplayTest extends BrowserTestBase {
     'field_test',
     'field_ui',
     'field_group',
-    'field_group_test',
+    'field_group_test'
   ];
 
   /**
@@ -157,8 +157,8 @@ public function testHtmlElement() {
     $this->drupalGet('node/' . $this->node->id());
 
     // Test group ids and classes.
-    $this->assertTrue($this->xpath("//div[contains(@id, 'wrapper-id')]"), 'Wrapper id set on wrapper div');
-    $this->assertTrue($this->xpath("//div[contains(@class, 'test-class')]"), 'Test class set on wrapper div, class="' . $group->group_name . ' test-class');
+    $this->assertCount(1, $this->xpath("//div[contains(@id, 'wrapper-id')]"), 'Wrapper id set on wrapper div');
+    $this->assertCount(1, $this->xpath("//div[contains(@class, 'test-class')]"), 'Test class set on wrapper div, class="' . $group->group_name . ' test-class');
 
     // Test group label.
     $this->assertSession()->responseNotContains('<h3><span>' . $data['label'] . '</span></h3>');
@@ -178,8 +178,8 @@ public function testHtmlElement() {
     field_group_group_save($group);
 
     $this->drupalGet('node/' . $this->node->id());
-    $this->assertTrue($this->xpath("//div[contains(@class, 'speed-fast')]"), 'Speed class is set');
-    $this->assertTrue($this->xpath("//div[contains(@class, 'effect-blink')]"), 'Effect class is set');
+    $this->assertCount(1, $this->xpath("//div[contains(@class, 'speed-fast')]"), 'Speed class is set');
+    $this->assertCount(1, $this->xpath("//div[contains(@class, 'effect-blink')]"), 'Effect class is set');
   }
 
   /**
@@ -205,8 +205,8 @@ public function testFieldset() {
     $this->drupalGet('node/' . $this->node->id());
 
     // Test group ids and classes.
-    $this->assertTrue($this->xpath("//fieldset[contains(@id, 'fieldset-id')]"), 'Correct id set on the fieldset');
-    $this->assertTrue($this->xpath("//fieldset[contains(@class, 'test-class')]"), 'Test class set on the fieldset');
+    $this->assertCount(1, $this->xpath("//fieldset[contains(@id, 'fieldset-id')]"), 'Correct id set on the fieldset');
+    $this->assertCount(1, $this->xpath("//fieldset[contains(@class, 'test-class')]"), 'Test class set on the fieldset');
   }
 
   /**
@@ -264,16 +264,16 @@ public function testTabs() {
     $this->drupalGet('node/' . $this->node->id());
 
     // Test properties.
-    $this->assertTrue($this->xpath("//div[contains(@class, 'test-class-wrapper')]"), 'Test class set on tabs wrapper');
-    $this->assertTrue($this->xpath("//details[contains(@class, 'test-class-2')]"), 'Test class set on second tab');
+    $this->assertCount(1, $this->xpath("//div[contains(@class, 'test-class-wrapper')]"), 'Test class set on tabs wrapper');
+    $this->assertCount(1, $this->xpath("//details[contains(@class, 'test-class-2')]"), 'Test class set on second tab');
     $this->assertSession()->responseContains('<div class="details-description">description of second tab</div>');
 
     // Test if correctly nested.
-    $this->assertTrue($this->xpath("//div[contains(@class, 'test-class-wrapper')]//details[contains(@class, 'test-class')]"), 'First tab is displayed as child of the wrapper.');
-    $this->assertTrue($this->xpath("//div[contains(@class, 'test-class-wrapper')]//details[contains(@class, 'test-class-2')]"), 'Second tab is displayed as child of the wrapper.');
+    $this->assertCount(2, $this->xpath("//div[contains(@class, 'test-class-wrapper')]//details[contains(@class, 'test-class')]"), 'First tab is displayed as child of the wrapper.');
+    $this->assertCount(1, $this->xpath("//div[contains(@class, 'test-class-wrapper')]//details[contains(@class, 'test-class-2')]"), 'Second tab is displayed as child of the wrapper.');
 
     // Test if it's a vertical tab.
-    $this->assertTrue($this->xpath('//div[@data-vertical-tabs-panes=""]'), 'Tabs are shown vertical.');
+    $this->assertCount(1, $this->xpath('//div[@data-vertical-tabs-panes=""]'), 'Tabs are shown vertical.');
 
     // Switch to horizontal.
     $tabs_group->format_settings['direction'] = 'horizontal';
@@ -282,7 +282,7 @@ public function testTabs() {
     $this->drupalGet('node/' . $this->node->id());
 
     // Test if it's a horizontal tab.
-    $this->assertTrue($this->xpath('//div[@data-horizontal-tabs-panes=""]'), 'Tabs are shown horizontal.');
+    $this->assertCount(1, $this->xpath('//div[@data-horizontal-tabs-panes=""]'), 'Tabs are shown horizontal.');
   }
 
   /**
@@ -338,15 +338,15 @@ public function testAccordion() {
     $this->drupalGet('node/' . $this->node->id());
 
     // Test properties.
-    $this->assertTrue($this->xpath("//div[contains(@class, 'test-class-wrapper')]"), 'Test class set on tabs wrapper');
-    $this->assertTrue($this->xpath("//div[contains(@class, 'effect-bounceslide')]"), 'Correct effect is set on the accordion');
-    $this->assertTrue($this->xpath("//div[contains(@class, 'test-class')]"), 'Accordion item with test-class is shown');
-    $this->assertTrue($this->xpath("//div[contains(@class, 'test-class-2')]"), 'Accordion item with test-class-2 is shown');
-    $this->assertTrue($this->xpath("//h3[contains(@class, 'field-group-accordion-active')]"), 'Accordion item 2 was set active');
+    $this->assertCount(1, $this->xpath("//div[contains(@class, 'test-class-wrapper')]"), 'Test class set on tabs wrapper');
+    $this->assertCount(1, $this->xpath("//div[contains(@class, 'effect-bounceslide')]"), 'Correct effect is set on the accordion');
+    $this->assertCount(3, $this->xpath("//div[contains(@class, 'test-class')]"), 'Accordion item with test-class is shown');
+    $this->assertCount(1, $this->xpath("//div[contains(@class, 'test-class-2')]"), 'Accordion item with test-class-2 is shown');
+    $this->assertCount(1, $this->xpath("//h3[contains(@class, 'field-group-accordion-active')]"), 'Accordion item 2 was set active');
 
     // Test if correctly nested.
-    $this->assertTrue($this->xpath("//div[contains(@class, 'test-class-wrapper')]//div[contains(@class, 'test-class')]"), 'First item is displayed as child of the wrapper.');
-    $this->assertTrue($this->xpath("//div[contains(@class, 'test-class-wrapper')]//div[contains(@class, 'test-class-2')]"), 'Second item is displayed as child of the wrapper.');
+    $this->assertCount(2, $this->xpath("//div[contains(@class, 'test-class-wrapper')]//div[contains(@class, 'test-class')]"), 'First item is displayed as child of the wrapper.');
+    $this->assertCount(1, $this->xpath("//div[contains(@class, 'test-class-wrapper')]//div[contains(@class, 'test-class-2')]"), 'Second item is displayed as child of the wrapper.');
   }
 
 }
diff --git a/web/modules/field_group/tests/src/Functional/FieldGroupTestTrait.php b/web/modules/field_group/tests/src/Functional/FieldGroupTestTrait.php
index 563ec92cd1..a4c2d77765 100644
--- a/web/modules/field_group/tests/src/Functional/FieldGroupTestTrait.php
+++ b/web/modules/field_group/tests/src/Functional/FieldGroupTestTrait.php
@@ -23,7 +23,7 @@ trait FieldGroupTestTrait {
    * @param array $data
    *   Data for the field group.
    *
-   * @return \stdClass
+   * @return object
    *   An object that represents the field group.
    */
   protected function createGroup($entity_type, $bundle, $context, $mode, array $data) {
@@ -34,7 +34,11 @@ protected function createGroup($entity_type, $bundle, $context, $mode, array $da
 
     $data['format_settings'] += Drupal::service('plugin.manager.field_group.formatters')->getDefaultSettings($data['format_type'], $context);
 
-    $group_name = 'group_' . mb_strtolower($this->randomMachineName());
+    $group_name_without_prefix = isset($data['group_name']) && is_string($data['group_name'])
+      ? preg_replace('/^group_/', '', $data['group_name'])
+      : mb_strtolower($this->randomMachineName());
+
+    $group_name = 'group_' . $group_name_without_prefix;
 
     $field_group = (object) [
       'group_name' => $group_name,
diff --git a/web/modules/field_group/tests/src/FunctionalJavascript/HorizontalTabsLabelsTest.php b/web/modules/field_group/tests/src/FunctionalJavascript/HorizontalTabsLabelsTest.php
new file mode 100644
index 0000000000..8c6701fc17
--- /dev/null
+++ b/web/modules/field_group/tests/src/FunctionalJavascript/HorizontalTabsLabelsTest.php
@@ -0,0 +1,288 @@
+<?php
+
+namespace Drupal\Tests\field_group\FunctionalJavascript;
+
+use Drupal\Core\Extension\Exception\UnknownExtensionException;
+use Drupal\Core\Extension\ThemeInstallerInterface;
+use Drupal\Core\Url;
+use Drupal\field\FieldStorageConfigInterface;
+use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
+use Drupal\Tests\field_group\Functional\FieldGroupTestTrait;
+
+/**
+ * Tests horizontal tabs labels.
+ *
+ * @group field_group
+ */
+class HorizontalTabsLabelsTest extends WebDriverTestBase {
+
+  use FieldGroupTestTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $defaultTheme = 'stark';
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = [
+    'block',
+    'field_group',
+    'node',
+    'user',
+  ];
+
+  /**
+   * The themes to test with.
+   *
+   * @var string[]
+   */
+  protected $themeList = [
+    'bartik',
+    'claro',
+    'classy',
+    'seven',
+    'stable',
+    'stable9',
+    'stark',
+  ];
+
+  /**
+   * The themes that are shipped with block configurations.
+   *
+   * @var string[]
+   */
+  protected $themesWithBlocks = [
+    'claro',
+  ];
+
+  /**
+   * The webassert session.
+   *
+   * @var \Drupal\Tests\WebAssert
+   */
+  protected $assertSession;
+
+  /**
+   * The page element.
+   *
+   * @var \Behat\Mink\Element\DocumentElement
+   */
+  protected $page;
+
+  /**
+   * The node type used for testing.
+   *
+   * @var \Drupal\node\NodeTypeInterface
+   */
+  protected $testNodeType;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    parent::setUp();
+
+    $this->assertSession = $this->assertSession();
+    $this->page = $this->getSession()->getPage();
+    $this->testNodeType = $this->drupalCreateContentType([
+      'type' => 'test_node_bundle',
+      'name' => 'Test Node Type',
+    ]);
+
+    // Add an extra field to the test content type.
+    $entity_type_manager = $this->container->get('entity_type.manager');
+    $field_storage = $entity_type_manager
+      ->getStorage('field_storage_config')
+      ->create([
+        'type' => 'string',
+        'field_name' => 'test_label',
+        'entity_type' => 'node',
+      ]);
+    assert($field_storage instanceof FieldStorageConfigInterface);
+    $field_storage->save();
+
+    $entity_type_manager->getStorage('field_config')
+      ->create([
+        'label' => 'Test label',
+        'field_storage' => $field_storage,
+        'bundle' => $this->testNodeType->id(),
+      ])
+      ->save();
+
+    $tab1 = [
+      'label' => 'Tab1',
+      'group_name' => 'group_tab1',
+      'weight' => '1',
+      'children' => [
+        0 => 'test_label',
+      ],
+      'format_type' => 'tab',
+      'format_settings' => [
+        'label' => 'Tab1',
+        'formatter' => 'open',
+      ],
+    ];
+    $this->createGroup('node', $this->testNodeType->id(), 'form', 'default', $tab1);
+    $this->createGroup('node', $this->testNodeType->id(), 'view', 'default', $tab1);
+
+    $tab2 = [
+      'label' => 'Tab2',
+      'group_name' => 'group_tab2',
+      'weight' => '2',
+      'children' => [
+        0 => 'body',
+      ],
+      'format_type' => 'tab',
+      'format_settings' => [
+        'label' => 'Tab2',
+        'formatter' => 'closed',
+      ],
+    ];
+    $this->createGroup('node', $this->testNodeType->id(), 'form', 'default', $tab2);
+    $this->createGroup('node', $this->testNodeType->id(), 'view', 'default', $tab2);
+
+    $horizontal_tabs = [
+      'label' => 'Horizontal tabs',
+      'group_name' => 'group_horizontal_tabs',
+      'weight' => '-5',
+      'children' => [
+        'group_tab1',
+        'group_tab2',
+      ],
+      'format_type' => 'tabs',
+      'format_settings' => [
+        'direction' => 'horizontal',
+        'label' => 'Horizontal tabs',
+      ],
+    ];
+    $this->createGroup('node', $this->testNodeType->id(), 'form', 'default', $horizontal_tabs);
+    $this->createGroup('node', $this->testNodeType->id(), 'view', 'default', $horizontal_tabs);
+
+    $entity_type_manager->getStorage('entity_form_display')
+      ->load(implode('.', [
+        'node',
+        $this->testNodeType->id(),
+        'default',
+      ]))
+      ->setComponent('test_label', ['weight' => '1'])
+      ->save();
+
+    $entity_type_manager->getStorage('entity_view_display')
+      ->load(implode('.', [
+        'node',
+        $this->testNodeType->id(),
+        'default',
+      ]))
+      ->setComponent('test_label', ['weight' => '1'])
+      ->save();
+  }
+
+  /**
+   * Tests horizontal tabs labels.
+   *
+   * @dataProvider providerTestHorizontalTabsLabels
+   */
+  public function testHorizontalTabsLabels(string $theme_name) {
+    if ($theme_name !== $this->defaultTheme) {
+      $theme_installer = \Drupal::service('theme_installer');
+      assert($theme_installer instanceof ThemeInstallerInterface);
+      try {
+        $theme_installer->install([$theme_name], TRUE);
+      }
+      catch (UnknownExtensionException $ex) {
+        // Themes might be missing, e.g Drupal 8.x does not have stable9 theme.
+        $this->pass("The $theme_name theme does not exist in the current test environment.");
+        return;
+      }
+      \Drupal::configFactory()
+        ->getEditable('system.theme')
+        ->set('default', $theme_name)
+        ->set('admin', $theme_name)
+        ->save();
+    }
+
+    if (!in_array($theme_name, $this->themesWithBlocks, TRUE)) {
+      $this->drupalPlaceBlock('page_title_block', [
+        'region' => 'content',
+      ]);
+      $this->drupalPlaceBlock('local_tasks_block', [
+        'region' => 'content',
+        'weight' => 1,
+      ]);
+      $this->drupalPlaceBlock('local_actions_block', [
+        'region' => 'content',
+        'weight' => 2,
+      ]);
+      $this->drupalPlaceBlock('system_main_block', [
+        'region' => 'content',
+        'weight' => 3,
+      ]);
+    }
+
+    $this->drupalLogin($this->rootUser);
+
+    // Actual test: check the node edit page. Tab1 and Tab2 should be present.
+    $this->drupalGet(Url::fromRoute('node.add', [
+      'node_type' => $this->testNodeType->id(),
+    ]));
+    $this->assertHorizontalTabsLabels();
+
+    // Create a node.
+    $this->page->fillField('title[0][value]', 'Field Group Horizontal Tabs Test Node');
+    $this->page->fillField('Test label', 'Test label');
+    $this->assertNotNull($tab2 = $this->page->find('css', '.js .field-group-tabs-wrapper a[href="#edit-group-tab2"]'));
+    $tab2->click();
+    $this->assertSession->waitForElementVisible('css', '[name="body[0][value]"]');
+    $this->page->fillField('body[0][value]', 'Donec laoreet imperdiet.');
+    $this->page->findButton('edit-submit')->click();
+    $this->assertSession->waitForElement('css', 'html.js [data-drupal-messages]');
+    $status_message = $this->page->find('css', 'html.js [data-drupal-messages]');
+    $this->assertStringContainsString("{$this->testNodeType->label()} Field Group Horizontal Tabs Test Node has been created.", $status_message->getText());
+
+    // Check the node.
+    $this->drupalGet(Url::fromRoute('entity.node.canonical', [
+      'node' => '1',
+    ]));
+    $this->assertHorizontalTabsLabels();
+
+    $this->drupalLogout();
+
+    // Retest the node with anonymous user.
+    $this->drupalGet(Url::fromRoute('entity.node.canonical', [
+      'node' => '1',
+    ]));
+    $this->assertHorizontalTabsLabels();
+  }
+
+  /**
+   * Asserts the horizontal tabs labels.
+   */
+  protected function assertHorizontalTabsLabels() {
+    $this->assertSession->waitForElement('css', '.js .field-group-tabs-wrapper a[href="#edit-group-tab1"]');
+    $this->assertSession->waitForElement('css', '.js .field-group-tabs-wrapper a[href="#edit-group-tab2"]');
+    $this->assertNotNull($tab1 = $this->page->find('css', '.js .field-group-tabs-wrapper a[href="#edit-group-tab1"]'));
+    $this->assertStringContainsString('Tab1', $tab1->getText());
+    $this->assertNotNull($tab2 = $this->page->find('css', '.js .field-group-tabs-wrapper a[href="#edit-group-tab2"]'));
+    $this->assertStringContainsString('Tab2', $tab2->getText());
+  }
+
+  /**
+   * Data provider for testHorizontalTabsLabels.
+   *
+   * @return string[][][]
+   *   The test cases with the theme machine names.
+   */
+  public function providerTestHorizontalTabsLabels() {
+    return array_reduce($this->themeList, function (array $carry, string $theme_name) {
+      $carry[$theme_name] = [
+        'theme_name' => $theme_name,
+      ];
+      return $carry;
+    }, []);
+  }
+
+}
-- 
GitLab