diff --git c/field_group.libraries.yml i/field_group.libraries.yml
index 21f8584..86a3610 100644
--- c/field_group.libraries.yml
+++ i/field_group.libraries.yml
@@ -17,6 +17,7 @@ core:
     - core/jquery
     - core/drupal
     - core/drupalSettings
+    - core/drupal.states
 
 formatter.html_element:
   js:
diff --git c/formatters/details/details.js i/formatters/details/details.js
index 43f82e5..68f2b2b 100644
--- c/formatters/details/details.js
+++ i/formatters/details/details.js
@@ -27,4 +27,19 @@
       );
     },
   };
+  // Hide the details field group if it is empty.
+  $(document).on('field_group:group_empty', (e, $group) => {
+    if (!$group.hasClass('field-group-details')) {
+      return;
+    }
+    $group.hide();
+  });
+
+  // Show the details field group if it is not empty.
+  $(document).on('field_group:group_not_empty', (e, $group) => {
+    if (!$group.hasClass('field-group-details')) {
+      return;
+    }
+    $group.show();
+  });
 })(jQuery, once);
diff --git c/formatters/fieldset/fieldset.js i/formatters/fieldset/fieldset.js
index f81a725..c611763 100644
--- c/formatters/fieldset/fieldset.js
+++ i/formatters/fieldset/fieldset.js
@@ -27,4 +27,19 @@
       );
     },
   };
+  // Hide the fieldset field group if it is empty.
+  $(document).on('field_group:group_empty', (e, $group) => {
+    if (!$group.hasClass('field-group-fieldset')) {
+      return;
+    }
+    $group.hide();
+  });
+
+  // Show the fieldset field group if it is not empty.
+  $(document).on('field_group:group_not_empty', (e, $group) => {
+    if (!$group.hasClass('field-group-fieldset')) {
+      return;
+    }
+    $group.show();
+  });
 })(jQuery);
diff --git c/formatters/html_element/html-element.js i/formatters/html_element/html-element.js
index 3d72237..4599451 100644
--- c/formatters/html_element/html-element.js
+++ i/formatters/html_element/html-element.js
@@ -74,4 +74,19 @@
       });
     },
   };
+  // Hide the html-element field group if it is empty.
+  $(document).on('field_group:group_empty', (e, $group) => {
+    if (!$group.hasClass('field-group-html-element')) {
+      return;
+    }
+    $group.hide();
+  });
+
+  // Show the html-element field group if it is not empty.
+  $(document).on('field_group:group_not_empty', (e, $group) => {
+    if (!$group.hasClass('field-group-html-element')) {
+      return;
+    }
+    $group.show();
+  });
 })(jQuery);
diff --git c/formatters/tabs/tabs.js i/formatters/tabs/tabs.js
index ff6afa8..73100be 100644
--- c/formatters/tabs/tabs.js
+++ i/formatters/tabs/tabs.js
@@ -7,6 +7,36 @@
   Drupal.FieldGroup = Drupal.FieldGroup || {};
   Drupal.FieldGroup.Effects = Drupal.FieldGroup.Effects || {};
 
+  /**
+   * Gets the Field Group Tab Object from the field group tab jQuery object.
+   *
+   * @param {jQuery} $group
+   *   A jQuery object representing the field group tab.
+   *
+   * @return {Drupal.horizontalTab|Drupal.verticalTab}
+   *   The horizontal or vertical tab object for the provided jQuery tab.
+   */
+  Drupal.FieldGroup.getFieldGroupTabObject = function ($group) {
+    if (typeof $group.data('horizontalTab') !== 'undefined') {
+      return $group.data('horizontalTab');
+    }
+    return $group.data('verticalTab');
+  };
+
+  /**
+   * Gets the tab button jQuery Object from the field group tab jQuery object.
+   *
+   * @param {jQuery} $group
+   *   A jQuery object representing the field group tab.
+   *
+   * @return {jQuery}
+   *   A jQuery object representing the field group tab button.
+   */
+  Drupal.FieldGroup.getTabButton = function ($group) {
+    const $object = Drupal.FieldGroup.getFieldGroupTabObject($group);
+    return $object.item;
+  };
+
   /**
    * Implements Drupal.FieldGroup.processHook().
    */
@@ -54,4 +84,38 @@
       }
     },
   };
+  // Hide tabs & tab field groups if empty.
+  $(document).on('field_group:group_empty', (e, $group) => {
+    if ($group.hasClass('field-group-tabs')) {
+      // Hide a tabs field group if it is empty.
+      $group.hide();
+      return;
+    }
+    if (!$group.hasClass('field-group-tab')) {
+      return;
+    }
+    // Hide a tab field group and button if it is empty.
+    $group.hide();
+    Drupal.FieldGroup.getTabButton($group).hide();
+  });
+
+  // Show tabs & tab field groups if not empty.
+  $(document).on('field_group:group_not_empty', (e, $group) => {
+    // Show a tabs field group if it is not empty.
+    if ($group.hasClass('field-group-tabs')) {
+      $group.show();
+      return;
+    }
+    if (!$group.hasClass('field-group-tab')) {
+      return;
+    }
+    // Only show a tab field group if it is not empty and its current
+    // button is selected.
+    const $button = Drupal.FieldGroup.getTabButton($group);
+    if ($button.hasClass('selected') || $button.hasClass('is-selected')) {
+      $group.show();
+    }
+    // Show the tab button if the tab field group is not empty.
+    $button.show();
+  });
 })(jQuery);
diff --git c/js/field_group.js i/js/field_group.js
index e807746..587bc1b 100644
--- c/js/field_group.js
+++ i/js/field_group.js
@@ -16,6 +16,91 @@
     Drupal.FieldGroup.groupWithFocus = element;
   };
 
+  /**
+   *  Determine if the current field group is empty.
+   *
+   * A field group is empty if all of its children are hidden.
+   *
+   * @param {jQuery} $group
+   *   A jQuery object representing the field group to be checked.
+   *
+   * @return {boolean}
+   *   Whether the field group is empty.
+   */
+  Drupal.FieldGroup.isGroupEmpty = ($group) => {
+    // Determine if the field group children are visible.
+    // If at least one child is visible, the group is NOT empty.
+    // Ensure we are not checking the children of nested groups.
+    const groupId = $group.attr('id');
+    const $visibleChildren = $group
+      .find('.field-group-child-field')
+      .not(`#${groupId} .field-group .field-group-child-field`)
+      .filter((i, el) => {
+        // To fix a bug on Safari 18.x we need to first check the style
+        // attribute on the element. Only if it is empty will we then call
+        // window.getComputedStyle(el).display`.
+        // It appears Safari is taking into account the parent's display value
+        // which in this case may be `none`. So the value returned could be
+        // `none` even though it is set to `block` on the element.
+        let elementDisplay = el.style.display;
+        if (elementDisplay === '') {
+          elementDisplay = window.getComputedStyle(el).display;
+        }
+        return (
+          elementDisplay !== 'none' && !el.classList.contains('visually-hidden')
+        );
+      });
+    return $visibleChildren.length === 0;
+  };
+
+  /**
+   * Evaluates and sets the visibility of a field group.
+   *
+   * This function checks if the provided field group is empty or not.
+   * Depending on the state, it will trigger an event on the document.
+   * If the group is empty, it triggers "field_group:group_empty", and if
+   * not, it triggers "field_group:group_not_empty".
+   *
+   * @param {jQuery} $group
+   *   The jQuery object representing the field group to evaluate and set
+   *   visibility for.
+   */
+  Drupal.FieldGroup.setFieldGroupVisibility = ($group) => {
+    if ($group.length !== 0) {
+      const groupEmpty = Drupal.FieldGroup.isGroupEmpty($group);
+      if (groupEmpty) {
+        $(document).trigger('field_group:group_empty', [$group]);
+      } else {
+        $(document).trigger('field_group:group_not_empty', [$group]);
+      }
+    }
+  };
+
+  /**
+   * Recursively sets the visibility for parent field groups of a given element.
+   *
+   * This function checks if the provided element is a child field and if so,
+   * it finds the closest parent field group and sets its visibility using
+   * Drupal.FieldGroup.setFieldGroupVisibility(). This process is repeated
+   * recursively for each parent field group found.
+   *
+   * @param {jQuery} $element
+   *   The jQuery element for which to set the parent field group visibility.
+   */
+  Drupal.FieldGroup.setParentFieldGroupVisibility = ($element) => {
+    if ($element.hasClass('field-group-child-field')) {
+      const $group = $element.parents('.field-group').first();
+      // If group is configured to be visible even when empty, return.
+      if ($group.hasClass('field-group-show-empty')) {
+        return;
+      }
+      if ($group.length) {
+        Drupal.FieldGroup.setFieldGroupVisibility($group);
+        Drupal.FieldGroup.setParentFieldGroupVisibility($group);
+      }
+    }
+  };
+
   /**
    * Behaviors.
    */
@@ -66,4 +151,14 @@
       });
     },
   };
+  // Check if Field Groups need to be hidden when the "state:visible" event
+  // is fired.
+  // NOTE: It is important this event listener and all "field_group:group_empty"
+  // and "field_group:group_not_empty" event listeners are declared outside
+  // the `attach` method as the "state:visible" event could be triggered during
+  // the Drupal.behaviors.states attach method, which would occur before the
+  // fieldGroup attach method.
+  $(document).on('state:visible', (e) => {
+    Drupal.FieldGroup.setParentFieldGroupVisibility($(e.target));
+  });
 })(jQuery, Drupal, drupalSettings);
diff --git c/modules/field_group_accordion/js/accordion.js i/modules/field_group_accordion/js/accordion.js
index c02cb04..4a6ce70 100644
--- c/modules/field_group_accordion/js/accordion.js
+++ i/modules/field_group_accordion/js/accordion.js
@@ -81,4 +81,26 @@
       });
     },
   };
+  // Hide the accordion field group and header if it is empty.
+  $(document).on('field_group:group_empty', (e, $group) => {
+    if (!$group.hasClass('field-group-accordion-item')) {
+      return;
+    }
+    $group.hide();
+    const headerId = $group.attr('aria-labelledby');
+    $(document).find(`#${headerId}`).hide();
+  });
+
+  // Show the accordion field group and header if it is not empty.
+  $(document).on('field_group:group_not_empty', (e, $group) => {
+    if (!$group.hasClass('field-group-accordion-item')) {
+      return;
+    }
+    // Only show the accordion group if it is the active group.
+    if ($group.hasClass('ui-accordion-content-active')) {
+      $group.show();
+    }
+    const headerId = $group.attr('aria-labelledby');
+    $(document).find(`#${headerId}`).show();
+  });
 })(jQuery);
diff --git c/modules/field_group_accordion/src/Plugin/field_group/FieldGroupFormatter/Accordion.php i/modules/field_group_accordion/src/Plugin/field_group/FieldGroupFormatter/Accordion.php
index a6b9409..95847e5 100644
--- c/modules/field_group_accordion/src/Plugin/field_group/FieldGroupFormatter/Accordion.php
+++ i/modules/field_group_accordion/src/Plugin/field_group/FieldGroupFormatter/Accordion.php
@@ -41,7 +41,15 @@ class Accordion extends FieldGroupFormatterBase {
 
     $classes = $this->getClasses();
     if (!empty($classes)) {
-      $element += ['#attributes' => ['class' => $classes]];
+      // Ensure any classes declared on the element are not overwritten.
+      if (isset($element['#attributes']['class'])) {
+        $element['#attributes']['class'] = array_merge($element['#attributes']['class'], $classes);
+      }
+      else {
+        $element += [
+          '#attributes' => ['class' => $classes],
+        ];
+      }
     }
 
   }
diff --git c/modules/field_group_accordion/src/Plugin/field_group/FieldGroupFormatter/AccordionItem.php i/modules/field_group_accordion/src/Plugin/field_group/FieldGroupFormatter/AccordionItem.php
index f598a6f..8fe7f81 100644
--- c/modules/field_group_accordion/src/Plugin/field_group/FieldGroupFormatter/AccordionItem.php
+++ i/modules/field_group_accordion/src/Plugin/field_group/FieldGroupFormatter/AccordionItem.php
@@ -49,7 +49,13 @@ class AccordionItem extends FieldGroupFormatterBase {
 
     $classes = $this->getClasses();
     if (!empty($classes)) {
-      $element += ['#attributes' => ['class' => $classes]];
+      // Ensure any classes declared on the element are not overwritten.
+      if (isset($element['#attributes']['class'])) {
+        $element['#attributes']['class'] = array_merge($element['#attributes']['class'], $classes);
+      }
+      else {
+        $element += ['#attributes' => ['class' => $classes]];
+      }
     }
 
     if ($this->getSetting('required_fields')) {
diff --git c/src/FieldGroupFormatterBase.php i/src/FieldGroupFormatterBase.php
index 4612b41..efdb565 100644
--- c/src/FieldGroupFormatterBase.php
+++ i/src/FieldGroupFormatterBase.php
@@ -185,11 +185,16 @@ abstract class FieldGroupFormatterBase extends PluginSettingsBase implements Fie
    */
   protected function getClasses() {
 
-    $classes = [];
+    $classes[] = 'field-group';
+    $classes[] = 'field-group-' . str_replace('_', '-', $this->getBaseId());
     // Add a required-fields class to trigger the js.
     if ($this->getSetting('required_fields')) {
       $classes[] = 'required-fields';
-      $classes[] = 'field-group-' . str_replace('_', '-', $this->getBaseId());
+    }
+    // Add a field-group-show-empty class to ensure group is visible even if
+    // all children are hidden by js.
+    if ($this->getSetting('show_empty_fields')) {
+      $classes[] = 'field-group-show-empty';
     }
 
     if ($this->getSetting('classes')) {
diff --git c/src/FormatterHelper.php i/src/FormatterHelper.php
index 8a09bf8..08f292d 100644
--- c/src/FormatterHelper.php
+++ i/src/FormatterHelper.php
@@ -98,6 +98,7 @@ class FormatterHelper implements TrustedCallbackInterface {
               continue;
             }
             $element[$child]['#group'] = $group_children_parent_group;
+            $element[$child]['#attributes']['class'][] = 'field-group-child-field';
           }
         }
       }
diff --git c/src/Plugin/field_group/FieldGroupFormatter/Details.php i/src/Plugin/field_group/FieldGroupFormatter/Details.php
index a25ae47..a5db48a 100644
--- c/src/Plugin/field_group/FieldGroupFormatter/Details.php
+++ i/src/Plugin/field_group/FieldGroupFormatter/Details.php
@@ -41,12 +41,18 @@ class Details extends FieldGroupFormatterBase {
 
     $classes = $this->getClasses();
     if (!empty($classes)) {
-      $element += [
-        '#attributes' => ['class' => $classes],
-      ];
+      // Ensure any classes declared on the element are not overwritten.
+      if (isset($element['#attributes']['class'])) {
+        $element['#attributes']['class'] = array_merge($element['#attributes']['class'], $classes);
+      }
+      else {
+        $element += [
+          '#attributes' => ['class' => $classes],
+        ];
+      }
     }
 
-    if ($this->getSetting('required_fields')) {
+    if ($this->context === 'form') {
       $element['#attached']['library'][] = 'field_group/formatter.details';
       $element['#attached']['library'][] = 'field_group/core';
     }
diff --git c/src/Plugin/field_group/FieldGroupFormatter/Fieldset.php i/src/Plugin/field_group/FieldGroupFormatter/Fieldset.php
index 09ad1cf..737bdac 100644
--- c/src/Plugin/field_group/FieldGroupFormatter/Fieldset.php
+++ i/src/Plugin/field_group/FieldGroupFormatter/Fieldset.php
@@ -49,10 +49,16 @@ class Fieldset extends FieldGroupFormatterBase {
 
     $classes = $this->getClasses();
     if (!empty($classes)) {
-      $element['#attributes'] += ['class' => $classes];
+      // Ensure any classes declared on the element are not overwritten.
+      if (isset($element['#attributes']['class'])) {
+        $element['#attributes']['class'] = array_merge($element['#attributes']['class'], $classes);
+      }
+      else {
+        $element['#attributes'] += ['class' => $classes];
+      }
     }
 
-    if ($this->getSetting('required_fields')) {
+    if ($this->context === 'form') {
       $element['#attached']['library'][] = 'field_group/formatter.fieldset';
       $element['#attached']['library'][] = 'field_group/core';
     }
diff --git c/src/Plugin/field_group/FieldGroupFormatter/HtmlElement.php i/src/Plugin/field_group/FieldGroupFormatter/HtmlElement.php
index 66a1104..2a0aa93 100644
--- c/src/Plugin/field_group/FieldGroupFormatter/HtmlElement.php
+++ i/src/Plugin/field_group/FieldGroupFormatter/HtmlElement.php
@@ -55,6 +55,10 @@ class HtmlElement extends FieldGroupFormatterBase {
 
     // Add the classes to the attributes array.
     $classes = $this->getClasses();
+    // Ensure any classes declared on the element are not overwritten.
+    if (isset($element['#attributes']['class'])) {
+      $classes = array_merge($classes, $element['#attributes']['class']);
+    }
     if (!empty($classes)) {
       if (!isset($element_attributes['class'])) {
         $element_attributes['class'] = [];
@@ -89,7 +93,7 @@ class HtmlElement extends FieldGroupFormatterBase {
       }
     }
 
-    if ($this->getSetting('required_fields')) {
+    if ($this->context === 'form') {
       $element['#attributes']['class'][] = 'field-group-html-element';
       $element['#attached']['library'][] = 'field_group/formatter.html_element';
       $element['#attached']['library'][] = 'field_group/core';
diff --git c/src/Plugin/field_group/FieldGroupFormatter/Tab.php i/src/Plugin/field_group/FieldGroupFormatter/Tab.php
index f4e98b3..91798aa 100644
--- c/src/Plugin/field_group/FieldGroupFormatter/Tab.php
+++ i/src/Plugin/field_group/FieldGroupFormatter/Tab.php
@@ -50,16 +50,22 @@ class Tab extends FieldGroupFormatterBase {
 
     $classes = $this->getClasses();
     if (!empty($classes)) {
-      $element += [
-        '#attributes' => ['class' => $classes],
-      ];
+      // Ensure any classes declared on the element are not overwritten.
+      if (isset($element['#attributes']['class'])) {
+        $element['#attributes']['class'] = array_merge($element['#attributes']['class'], $classes);
+      }
+      else {
+        $element += [
+          '#attributes' => ['class' => $classes],
+        ];
+      }
     }
 
     if ($this->getSetting('formatter') == 'open') {
       $element['#open'] = TRUE;
     }
 
-    if ($this->getSetting('required_fields')) {
+    if ($this->context === 'form') {
       $element['#attached']['library'][] = 'field_group/formatter.tabs';
       $element['#attached']['library'][] = 'field_group/core';
     }
diff --git c/src/Plugin/field_group/FieldGroupFormatter/Tabs.php i/src/Plugin/field_group/FieldGroupFormatter/Tabs.php
index 5a664e1..f64dbb5 100644
--- c/src/Plugin/field_group/FieldGroupFormatter/Tabs.php
+++ i/src/Plugin/field_group/FieldGroupFormatter/Tabs.php
@@ -33,8 +33,23 @@ class Tabs extends FieldGroupFormatterBase {
     // Keep using preRender parent for BC.
     parent::preRender($element, $processed_object);
 
+    // Ensure if the "field-group-child-field" class has been added as an
+    // element attribute, it is moved to the prefix classes to ensure proper
+    // hiding of the tabs if all children are hidden.
+    $element_classes = $element['#attributes']['class'] ?? NULL;
+    $prefix_classes = $this->getClasses();
+    if (NULL !== $element_classes) {
+      // Check if 'field-group-child-field' class exists in the element classes.
+      $key = array_search('field-group-child-field', $element_classes, TRUE);
+      if (FALSE !== $key) {
+        $prefix_classes[] = 'field-group-child-field';
+        // Remove 'field-group-child-field' class from element classes.
+        unset($element['#attributes']['class'][$key]);
+      }
+    }
+
     $element += [
-      '#prefix' => '<div class="' . implode(' ', $this->getClasses()) . '">',
+      '#prefix' => '<div class=" ' . implode(' ', $prefix_classes) . '">',
       '#suffix' => '</div>',
       '#tree' => TRUE,
       '#parents' => [$this->group->group_name],
diff --git c/tests/modules/field_group_test/field_group_test.module i/tests/modules/field_group_test/field_group_test.module
index 2823999..645e4c5 100644
--- c/tests/modules/field_group_test/field_group_test.module
+++ i/tests/modules/field_group_test/field_group_test.module
@@ -8,6 +8,7 @@
 use Drupal\Core\Access\AccessResult;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Session\AccountInterface;
 
 /**
@@ -28,3 +29,35 @@ function field_group_test_entity_field_access(
   return AccessResult::neutral();
 
 }
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ *
+ * Apply Javascript States to node form fields to hide fields when a specific
+ * trigger field has a value. This will enable us to test the hiding of field
+ * groups when all contained fields are hidden.
+ *
+ * @see \Drupal\Tests\field_group\FunctionalJavascript\FieldGroupJavascriptStatesTest
+ */
+function field_group_test_form_node_test_content_form_alter(&$form, FormStateInterface $form_state): void {
+  $form['field_hidden_by_trigger_a_single']['#states'] = [
+    'visible' => [
+      ':input[name="field_test_trigger_a[0][value]"]' => ['value' => ''],
+    ],
+  ];
+  $form['field_hidden_by_trigger_a_nested']['#states'] = [
+    'visible' => [
+      ':input[name="field_test_trigger_a[0][value]"]' => ['value' => ''],
+    ],
+  ];
+  $form['field_hidden_by_trigger_a_comb']['#states'] = [
+    'visible' => [
+      ':input[name="field_test_trigger_a[0][value]"]' => ['value' => ''],
+    ],
+  ];
+  $form['field_hidden_by_trigger_b_comb']['#states'] = [
+    'visible' => [
+      ':input[name="field_test_trigger_b[0][value]"]' => ['value' => ''],
+    ],
+  ];
+}
diff --git c/tests/src/FunctionalJavascript/FieldGroupJavascriptStatesTest.php i/tests/src/FunctionalJavascript/FieldGroupJavascriptStatesTest.php
new file mode 100644
index 0000000..a6e148e
--- /dev/null
+++ i/tests/src/FunctionalJavascript/FieldGroupJavascriptStatesTest.php
@@ -0,0 +1,771 @@
+<?php
+
+namespace Drupal\Tests\field_group\FunctionalJavascript;
+
+use Drupal\Core\Entity\Entity\EntityFormDisplay;
+use Drupal\field\Entity\FieldConfig;
+use Drupal\field\Entity\FieldStorageConfig;
+use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
+use Drupal\Tests\field_group\Functional\FieldGroupTestTrait;
+
+/**
+ * Tests the JavaScript States functionality of the Field Group module.
+ *
+ * Specifically assert that hiding of all fields in a group via javascript
+ * states, also hides the parent field groups.
+ *
+ * @group field_group
+ */
+class FieldGroupJavascriptStatesTest extends WebDriverTestBase {
+
+  use FieldGroupTestTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $defaultTheme = 'stark';
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  protected static $modules = [
+    'node',
+    'field_test',
+    'field_group',
+    'field_group_test',
+  ];
+
+  /**
+   * The content type to be used in this test.
+   *
+   * @var string
+   */
+  protected $contentType = 'test_content';
+
+  /**
+   * The field names to be used in these tests.
+   *
+   * @var string
+   */
+  protected $fieldNames = [
+    'field_test_trigger_a',
+    'field_test_trigger_b',
+    'field_hidden_by_trigger_a_single',
+    'field_hidden_by_trigger_a_nested',
+    'field_hidden_by_trigger_a_comb',
+    'field_hidden_by_trigger_b_comb',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp(): void {
+    parent::setUp();
+    $this->drupalCreateContentType([
+      'type' => $this->contentType,
+      'name' => 'Test content',
+    ]);
+
+    // Add time fields to test content type.
+    foreach ($this->fieldNames as $field_name) {
+      $field_storage = FieldStorageConfig::create([
+        'field_name' => $field_name,
+        'entity_type' => 'node',
+        'type' => 'test_field',
+      ]);
+      $field_storage->save();
+      $field = FieldConfig::create([
+        'field_storage' => $field_storage,
+        'bundle' => $this->contentType,
+        'required' => FALSE,
+      ]);
+      $field->save();
+
+      // Configure the widget to make sure field is shown.
+      /** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display */
+      $form_display = EntityFormDisplay::load('node.' . $this->contentType . '.default');
+
+      // Set the field visible on the form display object.
+      $display_options = [
+        'type' => 'string_textfield',
+        'region' => 'content',
+        'settings' => [
+          'size' => 60,
+        ],
+      ];
+      $form_display->setComponent($field_name, $display_options);
+
+      // Save the form display.
+      $form_display->save();
+    }
+
+    // Create test user for creating test nodes.
+    $this->drupalLogin($this->drupalCreateUser([
+      'create ' . $this->contentType . ' content',
+    ]));
+  }
+
+  /**
+   * Test Field Group Tabs Javascript States hide functionality.
+   */
+  public function testJavascriptStatesHideFieldGroupTabs(): void {
+    $data = [
+      'label' => 'Single',
+      'group_name' => 'group_single',
+      'children' => [
+        0 => 'field_hidden_by_trigger_a_single',
+      ],
+      'format_type' => 'tab',
+      'format_settings' => [
+        'label' => 'Single',
+        'formatter' => 'open',
+      ],
+    ];
+    $single = $this->createGroup('node', $this->contentType, 'form', 'default', $data);
+
+    $data = [
+      'label' => 'Field group details',
+      'group_name' => 'group_details',
+      'children' => [
+        0 => 'field_hidden_by_trigger_a_nested',
+      ],
+      'format_type' => 'details',
+      'format_settings' => [
+        'open' => TRUE,
+      ],
+    ];
+    $field_group_details = $this->createGroup('node', $this->contentType, 'form', 'default', $data);
+
+    $data = [
+      'label' => 'Nested',
+      'group_name' => 'group_nested',
+      'children' => [
+        0 => $field_group_details->group_name,
+      ],
+      'format_type' => 'tab',
+      'format_settings' => [
+        'label' => 'Nested',
+        'formatter' => 'closed',
+      ],
+    ];
+    $nested = $this->createGroup('node', $this->contentType, 'form', 'default', $data);
+
+    $data = [
+      'label' => 'Combined',
+      'group_name' => 'group_combined',
+      'children' => [
+        0 => 'field_hidden_by_trigger_a_comb',
+        1 => 'field_hidden_by_trigger_b_comb',
+      ],
+      'format_type' => 'tab',
+      'format_settings' => [
+        'label' => 'Combined',
+        'formatter' => 'closed',
+      ],
+    ];
+    $combined = $this->createGroup('node', $this->contentType, 'form', 'default', $data);
+
+    $data = [
+      'label' => 'Tabs',
+      'group_name' => 'group_tabs',
+      'children' => [
+        0 => $single->group_name,
+        1 => $nested->group_name,
+        2 => $combined->group_name,
+      ],
+      'format_type' => 'tabs',
+      'format_settings' => [
+        'direction' => 'vertical',
+        'label' => 'Tabs',
+      ],
+    ];
+    $tabs_group = $this->createGroup('node', $this->contentType, 'form', 'default', $data);
+
+    $this->assertTabsStatesFunctionality();
+
+    // Switch to horizontal.
+    $tabs_group->format_settings['direction'] = 'horizontal';
+    field_group_group_save($tabs_group);
+
+    $this->assertTabsStatesFunctionality();
+  }
+
+  /**
+   * Test Field Group HTML Element Javascript States hide functionality.
+   */
+  public function testJavascriptStatesHideFieldGroupHtmlElement(): void {
+    $data = [
+      'label' => 'Single',
+      'group_name' => 'group_single',
+      'children' => [
+        0 => 'field_hidden_by_trigger_a_single',
+      ],
+      'format_type' => 'html_element',
+      'format_settings' => [
+        'label' => 'Single',
+        'element' => 'div',
+        'id' => 'single-id',
+      ],
+    ];
+    $this->createGroup('node', $this->contentType, 'form', 'default', $data);
+
+    $data = [
+      'label' => 'Child Group',
+      'group_name' => 'group_child',
+      'children' => [
+        0 => 'field_hidden_by_trigger_a_nested',
+      ],
+      'format_type' => 'html_element',
+      'format_settings' => [
+        'label' => 'Child Group',
+        'element' => 'div',
+        'id' => 'child-id',
+      ],
+    ];
+    $child_group = $this->createGroup('node', $this->contentType, 'form', 'default', $data);
+
+    $data = [
+      'label' => 'Nested',
+      'group_name' => 'group_nested',
+      'children' => [
+        0 => $child_group->group_name,
+      ],
+      'format_type' => 'html_element',
+      'format_settings' => [
+        'label' => 'Nested',
+        'element' => 'div',
+        'id' => 'nested-id',
+      ],
+    ];
+    $this->createGroup('node', $this->contentType, 'form', 'default', $data);
+
+    $data = [
+      'label' => 'Combined',
+      'group_name' => 'group_combined',
+      'children' => [
+        0 => 'field_hidden_by_trigger_a_comb',
+        1 => 'field_hidden_by_trigger_b_comb',
+      ],
+      'format_type' => 'html_element',
+      'format_settings' => [
+        'label' => 'Combined',
+        'element' => 'div',
+        'id' => 'combined-id',
+      ],
+    ];
+    $this->createGroup('node', $this->contentType, 'form', 'default', $data);
+
+    $this->assertFieldGroupsStatesFunctionality('div');
+  }
+
+  /**
+   * Test Field Group Fieldset Javascript States hide functionality.
+   */
+  public function testJavascriptStatesHideFieldGroupFieldset(): void {
+    $data = [
+      'label' => 'Single',
+      'group_name' => 'group_single',
+      'children' => [
+        0 => 'field_hidden_by_trigger_a_single',
+      ],
+      'format_type' => 'fieldset',
+      'format_settings' => [
+        'id' => 'single-id',
+      ],
+    ];
+    $this->createGroup('node', $this->contentType, 'form', 'default', $data);
+
+    $data = [
+      'label' => 'Child Group',
+      'group_name' => 'group_child',
+      'children' => [
+        0 => 'field_hidden_by_trigger_a_nested',
+      ],
+      'format_type' => 'fieldset',
+      'format_settings' => [
+        'id' => 'child-id',
+      ],
+    ];
+    $child_group = $this->createGroup('node', $this->contentType, 'form', 'default', $data);
+
+    $data = [
+      'label' => 'Nested',
+      'group_name' => 'group_nested',
+      'children' => [
+        0 => $child_group->group_name,
+      ],
+      'format_type' => 'fieldset',
+      'format_settings' => [
+        'id' => 'nested-id',
+      ],
+    ];
+    $this->createGroup('node', $this->contentType, 'form', 'default', $data);
+
+    $data = [
+      'label' => 'Combined',
+      'group_name' => 'group_combined',
+      'children' => [
+        0 => 'field_hidden_by_trigger_a_comb',
+        1 => 'field_hidden_by_trigger_b_comb',
+      ],
+      'format_type' => 'fieldset',
+      'format_settings' => [
+        'id' => 'combined-id',
+      ],
+    ];
+    $this->createGroup('node', $this->contentType, 'form', 'default', $data);
+
+    $this->assertFieldGroupsStatesFunctionality('fieldset');
+  }
+
+  /**
+   * Test Field Group Details Javascript States hide functionality.
+   */
+  public function testJavascriptStatesHideFieldGroupDetails(): void {
+    $data = [
+      'label' => 'Single',
+      'group_name' => 'group_single',
+      'children' => [
+        0 => 'field_hidden_by_trigger_a_single',
+      ],
+      'format_type' => 'details',
+      'format_settings' => [
+        'id' => 'single-id',
+        'open' => TRUE,
+      ],
+    ];
+    $this->createGroup('node', $this->contentType, 'form', 'default', $data);
+
+    $data = [
+      'label' => 'Child Group',
+      'group_name' => 'group_child',
+      'children' => [
+        0 => 'field_hidden_by_trigger_a_nested',
+      ],
+      'format_type' => 'details',
+      'format_settings' => [
+        'id' => 'child-id',
+        'open' => TRUE,
+      ],
+    ];
+    $child_group = $this->createGroup('node', $this->contentType, 'form', 'default', $data);
+
+    $data = [
+      'label' => 'Nested',
+      'group_name' => 'group_nested',
+      'children' => [
+        0 => $child_group->group_name,
+      ],
+      'format_type' => 'details',
+      'format_settings' => [
+        'id' => 'nested-id',
+        'open' => TRUE,
+      ],
+    ];
+    $this->createGroup('node', $this->contentType, 'form', 'default', $data);
+
+    $data = [
+      'label' => 'Combined',
+      'group_name' => 'group_combined',
+      'children' => [
+        0 => 'field_hidden_by_trigger_a_comb',
+        1 => 'field_hidden_by_trigger_b_comb',
+      ],
+      'format_type' => 'details',
+      'format_settings' => [
+        'id' => 'combined-id',
+        'open' => TRUE,
+      ],
+    ];
+    $this->createGroup('node', $this->contentType, 'form', 'default', $data);
+
+    $this->assertFieldGroupsStatesFunctionality('details');
+  }
+
+  /**
+   * Test Field Group Javascript States functionality respects display empty.
+   *
+   * Ensure option `Display element also when empty` is respected.
+   */
+  public function testJavascriptStatesHideDisplayEmpty(): void {
+    $data = [
+      'label' => 'Show Empty Group',
+      'group_name' => 'group_show_empty',
+      'children' => [
+        0 => 'field_hidden_by_trigger_a_single',
+      ],
+      'format_type' => 'fieldset',
+      'format_settings' => [
+        'id' => 'show-empty-id',
+        'show_empty_fields' => TRUE,
+      ],
+    ];
+    $this->createGroup('node', $this->contentType, 'form', 'default', $data);
+
+    // Load the node creation page.
+    $this->drupalGet('node/add/' . $this->contentType);
+    $page = $this->getSession()->getPage();
+
+    // Find trigger.
+    $trigger_a = $page->findField('field_test_trigger_a');
+    $this->assertNotEmpty($trigger_a);
+    $this->assertTrue($trigger_a->isVisible());
+
+    // Find Show Empty Group and contained field_hidden_by_trigger_a_single.
+    $show_empty_group = $page->find('css', 'fieldset#show-empty-id');
+    $field_hidden_by_trigger_a_single = $page->findField('field_hidden_by_trigger_a_single');
+    $this->assertNotEmpty($show_empty_group);
+    $this->assertNotEmpty($field_hidden_by_trigger_a_single);
+    $this->assertTrue($show_empty_group->isVisible());
+    $this->assertTrue($field_hidden_by_trigger_a_single->isVisible());
+
+    // Assert hiding of Groups based on states hidden fields.
+    // Add content to trigger A which should hide:
+    // - field_hidden_by_trigger_a_single.
+    $trigger_a->setValue('filled');
+    // Show Empty group should still be visible even if its field is hidden.
+    $this->assertTrue($show_empty_group->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_a_single->isVisible());
+
+    // Remove content from trigger A which should make visible:
+    // - field_hidden_by_trigger_a_single.
+    $trigger_a->setValue('');
+    $this->assertTrue($show_empty_group->isVisible());
+    $this->assertTrue($field_hidden_by_trigger_a_single->isVisible());
+  }
+
+  /**
+   * Assert Tabs are hidden when all contain fields are hidden by states.
+   */
+  protected function assertTabsStatesFunctionality(): void {
+
+    // Load the node creation page.
+    $this->drupalGet('node/add/' . $this->contentType);
+    $page = $this->getSession()->getPage();
+
+    // Find triggers.
+    $trigger_a = $page->findField('field_test_trigger_a');
+    $trigger_b = $page->findField('field_test_trigger_b');
+    $this->assertNotEmpty($trigger_a);
+    $this->assertNotEmpty($trigger_b);
+    $this->assertTrue($trigger_a->isVisible());
+    $this->assertTrue($trigger_b->isVisible());
+
+    // Find Single Tab and contained field_hidden_by_trigger_a_single.
+    $tab_single = $page->find('css', '.field-group-tabs-wrapper a[href="#edit-group-single"]');
+    $field_hidden_by_trigger_a_single = $page->findField('field_hidden_by_trigger_a_single');
+    $this->assertNotEmpty($tab_single);
+    $this->assertNotEmpty($field_hidden_by_trigger_a_single);
+    $this->assertTrue($tab_single->isVisible());
+    $this->assertTrue($field_hidden_by_trigger_a_single->isVisible());
+
+    // Find Nested Tab and contained Details group and
+    // field_hidden_by_trigger_a_nested.
+    $tab_nested = $page->find('css', '.field-group-tabs-wrapper a[href="#edit-group-nested"]');
+    $details = $page->find('css', '.field-group-tabs-wrapper #edit-group-details');
+    $field_hidden_by_trigger_a_nested = $page->findField('field_hidden_by_trigger_a_nested');
+    $this->assertNotEmpty($field_hidden_by_trigger_a_nested);
+    $this->assertNotEmpty($tab_nested);
+    $this->assertNotEmpty($details);
+    $this->assertTrue($tab_nested->isVisible());
+    // Because tab is closed both Details group and field should not be visible.
+    $this->assertFalse($details->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_a_nested->isVisible());
+
+    // Find combined Tab and contained field_hidden_by_trigger_a_comb and
+    // field_hidden_by_trigger_b_comb.
+    $tab_combined = $page->find('css', '.field-group-tabs-wrapper a[href="#edit-group-combined"]');
+    $field_hidden_by_trigger_a_comb = $page->findField('field_hidden_by_trigger_a_comb');
+    $field_hidden_by_trigger_b_comb = $page->findField('field_hidden_by_trigger_b_comb');
+    $this->assertNotEmpty($tab_combined);
+    $this->assertNotEmpty($field_hidden_by_trigger_a_comb);
+    $this->assertNotEmpty($field_hidden_by_trigger_b_comb);
+    $this->assertTrue($tab_combined->isVisible());
+    // Because tab is closed both fields should not be visible.
+    $this->assertFalse($field_hidden_by_trigger_a_comb->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_b_comb->isVisible());
+
+    // Click through tabs to ensure field visibility it correctly configured.
+    // Click Tab Nested.
+    $tab_nested->click();
+    // Tab Single content is no longer visible.
+    $this->assertTrue($tab_single->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_a_single->isVisible());
+    // Tab Nested content is now visible.
+    $this->assertTrue($tab_nested->isVisible());
+    $this->assertTrue($details->isVisible());
+    $this->assertTrue($field_hidden_by_trigger_a_nested->isVisible());
+    // Tab Combined content is still not visible.
+    $this->assertTrue($tab_combined->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_a_comb->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_b_comb->isVisible());
+
+    // Click Tab Combined.
+    $tab_combined->click();
+    // Tab Single content is still not visible.
+    $this->assertTrue($tab_single->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_a_single->isVisible());
+    // Tab Nested content is no longer visible.
+    $this->assertTrue($tab_nested->isVisible());
+    $this->assertFalse($details->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_a_nested->isVisible());
+    // Tab Combined content is now visible.
+    $this->assertTrue($tab_combined->isVisible());
+    $this->assertTrue($field_hidden_by_trigger_a_comb->isVisible());
+    $this->assertTrue($field_hidden_by_trigger_b_comb->isVisible());
+
+    // Click Tab Single.
+    $tab_single->click();
+    // Tab Single content is now visible.
+    $this->assertTrue($tab_single->isVisible());
+    $this->assertTrue($field_hidden_by_trigger_a_single->isVisible());
+    // Tab Nested content is still not visible.
+    $this->assertTrue($tab_nested->isVisible());
+    $this->assertFalse($details->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_a_nested->isVisible());
+    // Tab Combined content is no longer visible.
+    $this->assertTrue($tab_combined->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_a_comb->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_b_comb->isVisible());
+
+    // Assert hiding of Groups based on states hidden fields.
+    // Add content to trigger A which should hide:
+    // - field_hidden_by_trigger_a_single
+    // - field_hidden_by_trigger_a_nested
+    // - field_hidden_by_trigger_a_comb.
+    $trigger_a->setValue('filled');
+    // Tab Single should no longer be visible because its field is hidden.
+    $this->assertFalse($tab_single->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_a_single->isVisible());
+    // Tab Nested and contained Details group should no longer be visible
+    // because its field is hidden.
+    $this->assertFalse($tab_nested->isVisible());
+    $this->assertFalse($details->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_a_nested->isVisible());
+    // Tab Combined should still be visible because only one field is hidden.
+    // It will still be closed, however.
+    $this->assertTrue($tab_combined->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_a_comb->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_b_comb->isVisible());
+
+    // Click Tab Combined.
+    $tab_combined->click();
+    // Tab Combined content is now open, but field_hidden_by_trigger_a_comb
+    // is now hidden.
+    $this->assertTrue($tab_combined->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_a_comb->isVisible());
+    $this->assertTrue($field_hidden_by_trigger_b_comb->isVisible());
+
+    // Add content to trigger B which should hide
+    // - field_hidden_by_trigger_b_comb.
+    $trigger_b->setValue('filled');
+    // Tab Single should still be hidden.
+    $this->assertFalse($tab_single->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_a_single->isVisible());
+    // Tab Nested and contained Details group should still be hidden.
+    $this->assertFalse($tab_nested->isVisible());
+    $this->assertFalse($details->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_a_nested->isVisible());
+    // Tab Combined and fields should no longer be visible.
+    $this->assertFalse($tab_combined->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_a_comb->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_b_comb->isVisible());
+
+    // Remove content from trigger A which should make visible:
+    // - field_hidden_by_trigger_a_single
+    // - field_hidden_by_trigger_a_nested
+    // - field_hidden_by_trigger_a_comb.
+    $trigger_a->setValue('');
+    // Tab Single should now be visible but closed.
+    $this->assertTrue($tab_single->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_a_single->isVisible());
+    // Tab Nested should now be visible but closed.
+    $this->assertTrue($tab_nested->isVisible());
+    $this->assertFalse($details->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_a_nested->isVisible());
+    // Tab Combined should now be visible and open.
+    $this->assertTrue($tab_combined->isVisible());
+    $this->assertTrue($field_hidden_by_trigger_a_comb->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_b_comb->isVisible());
+
+    // Click through tabs to assert field visibility.
+    // Click Tab Combined.
+    $tab_single->click();
+    // Tab Single content is now open.
+    $this->assertTrue($tab_single->isVisible());
+    $this->assertTrue($field_hidden_by_trigger_a_single->isVisible());
+    // Tab Nested content is still closed.
+    $this->assertTrue($tab_nested->isVisible());
+    $this->assertFalse($details->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_a_nested->isVisible());
+    // Tab Combined content is now closed.
+    $this->assertTrue($tab_combined->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_a_comb->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_b_comb->isVisible());
+
+    // Click Tab Nested.
+    $tab_nested->click();
+    // Tab Single is now closed.
+    $this->assertTrue($tab_single->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_a_single->isVisible());
+    // Tab Nested content is now open.
+    $this->assertTrue($tab_nested->isVisible());
+    $this->assertTrue($details->isVisible());
+    $this->assertTrue($field_hidden_by_trigger_a_nested->isVisible());
+    // Tab Combined content is still closed.
+    $this->assertTrue($tab_combined->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_a_comb->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_b_comb->isVisible());
+
+    // Remove content from trigger B which should make visible:
+    // - field_hidden_by_trigger_b_comb.
+    $trigger_b->setValue('');
+    // Tab Single still closed.
+    $this->assertTrue($tab_single->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_a_single->isVisible());
+    // Tab Nested still open.
+    $this->assertTrue($tab_nested->isVisible());
+    $this->assertTrue($details->isVisible());
+    $this->assertTrue($field_hidden_by_trigger_a_nested->isVisible());
+    // Tab Combined still closed.
+    $this->assertTrue($tab_combined->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_a_comb->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_b_comb->isVisible());
+
+    // Click Tab Combined.
+    $tab_combined->click();
+    // Tab Single content is still closed.
+    $this->assertTrue($tab_single->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_a_single->isVisible());
+    // Tab Nested content is now closed.
+    $this->assertTrue($tab_nested->isVisible());
+    $this->assertFalse($details->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_a_nested->isVisible());
+    // Tab Combined now open and all fields are now visible.
+    $this->assertTrue($tab_combined->isVisible());
+    $this->assertTrue($field_hidden_by_trigger_a_comb->isVisible());
+    $this->assertTrue($field_hidden_by_trigger_b_comb->isVisible());
+
+  }
+
+  /**
+   * Assert Groups are hidden when all contain fields are hidden by states.
+   *
+   * @param string $group_element
+   *   The field group element.
+   */
+  protected function assertFieldGroupsStatesFunctionality(string $group_element): void {
+    // Load the node creation page.
+    $this->drupalGet('node/add/' . $this->contentType);
+    $page = $this->getSession()->getPage();
+
+    // Find triggers.
+    $trigger_a = $page->findField('field_test_trigger_a');
+    $trigger_b = $page->findField('field_test_trigger_b');
+    $this->assertNotEmpty($trigger_a);
+    $this->assertNotEmpty($trigger_b);
+    $this->assertTrue($trigger_a->isVisible());
+    $this->assertTrue($trigger_b->isVisible());
+
+    // Find Single Group and contained field_hidden_by_trigger_a_single.
+    $single_group = $page->find('css', $group_element . '#single-id');
+    $field_hidden_by_trigger_a_single = $page->findField('field_hidden_by_trigger_a_single');
+    $this->assertNotEmpty($single_group);
+    $this->assertNotEmpty($field_hidden_by_trigger_a_single);
+    $this->assertTrue($single_group->isVisible());
+    $this->assertTrue($field_hidden_by_trigger_a_single->isVisible());
+
+    // Find Nested group and contained child group and
+    // field_hidden_by_trigger_a_nested.
+    $nested_group = $page->find('css', $group_element . '#nested-id');
+    $child_group = $page->find('css', $group_element . '#child-id');
+    $field_hidden_by_trigger_a_nested = $page->findField('field_hidden_by_trigger_a_nested');
+    $this->assertNotEmpty($field_hidden_by_trigger_a_nested);
+    $this->assertNotEmpty($nested_group);
+    $this->assertNotEmpty($child_group);
+    $this->assertTrue($nested_group->isVisible());
+    $this->assertTrue($child_group->isVisible());
+    $this->assertTrue($field_hidden_by_trigger_a_nested->isVisible());
+
+    // Find Combined group and contained field_hidden_by_trigger_a_comb and
+    // field_hidden_by_trigger_b_comb.
+    $combined_group = $page->find('css', $group_element . '#combined-id');
+    $field_hidden_by_trigger_a_comb = $page->findField('field_hidden_by_trigger_a_comb');
+    $field_hidden_by_trigger_b_comb = $page->findField('field_hidden_by_trigger_b_comb');
+    $this->assertNotEmpty($combined_group);
+    $this->assertNotEmpty($field_hidden_by_trigger_a_comb);
+    $this->assertNotEmpty($field_hidden_by_trigger_b_comb);
+    $this->assertTrue($combined_group->isVisible());
+    $this->assertTrue($field_hidden_by_trigger_a_comb->isVisible());
+    $this->assertTrue($field_hidden_by_trigger_b_comb->isVisible());
+
+    // Assert hiding of Groups based on states hidden fields.
+    // Add content to trigger A which should hide:
+    // - field_hidden_by_trigger_a_single
+    // - field_hidden_by_trigger_a_nested
+    // - field_hidden_by_trigger_a_comb.
+    $trigger_a->setValue('filled');
+    // Single group should no longer be visible because its field is hidden.
+    $this->assertFalse($single_group->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_a_single->isVisible());
+    // Nested group and contained Child group should no longer be visible
+    // because its field is hidden.
+    $this->assertFalse($nested_group->isVisible());
+    $this->assertFalse($child_group->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_a_nested->isVisible());
+    // Combined group should still be visible because only one field is hidden.
+    $this->assertTrue($combined_group->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_a_comb->isVisible());
+    $this->assertTrue($field_hidden_by_trigger_b_comb->isVisible());
+
+    // Add content to trigger B which should hide
+    // - field_hidden_by_trigger_b_comb.
+    $trigger_b->setValue('filled');
+    // Single group should still be hidden.
+    $this->assertFalse($single_group->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_a_single->isVisible());
+    // Nested group and contained Child group should still be hidden.
+    $this->assertFalse($nested_group->isVisible());
+    $this->assertFalse($child_group->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_a_nested->isVisible());
+    // Combined group is now hidden because all fields are hidden.
+    $this->assertFalse($combined_group->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_a_comb->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_b_comb->isVisible());
+
+    // Remove content from trigger A which should make visible:
+    // - field_hidden_by_trigger_a_single
+    // - field_hidden_by_trigger_a_nested
+    // - field_hidden_by_trigger_a_comb.
+    $trigger_a->setValue('');
+    // Single group should now be visible.
+    $this->assertTrue($single_group->isVisible());
+    $this->assertTrue($field_hidden_by_trigger_a_single->isVisible());
+    // Nested group and contained Child group is now visible.
+    $this->assertTrue($nested_group->isVisible());
+    $this->assertTrue($child_group->isVisible());
+    $this->assertTrue($field_hidden_by_trigger_a_nested->isVisible());
+    // Combined group should now be visible but one field is still hidden.
+    $this->assertTrue($combined_group->isVisible());
+    $this->assertTrue($field_hidden_by_trigger_a_comb->isVisible());
+    $this->assertFalse($field_hidden_by_trigger_b_comb->isVisible());
+
+    // Remove content from trigger B which should make visible:
+    // - field_hidden_by_trigger_b_comb.
+    $trigger_b->setValue('');
+    // Single group still visible.
+    $this->assertTrue($single_group->isVisible());
+    $this->assertTrue($field_hidden_by_trigger_a_single->isVisible());
+    // Nested group and contained Child group still visible.
+    $this->assertTrue($nested_group->isVisible());
+    $this->assertTrue($child_group->isVisible());
+    $this->assertTrue($field_hidden_by_trigger_a_nested->isVisible());
+    // Combined group should still visible and all fields are now visible.
+    $this->assertTrue($combined_group->isVisible());
+    $this->assertTrue($field_hidden_by_trigger_a_comb->isVisible());
+    $this->assertTrue($field_hidden_by_trigger_b_comb->isVisible());
+  }
+
+}
