From a39fdb4ada25f9cc97dda63ed8e6da3660eaf3b5 Mon Sep 17 00:00:00 2001 From: Steven Jones <steven.jones@computerminds.co.uk> Date: Tue, 11 Mar 2025 16:47:20 +0000 Subject: [PATCH 1/2] Issue #3197504 by jrockowitz, steven jones: Initial stab at getting something working. --- src/WebformSubmissionConditionsValidator.php | 126 +++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/src/WebformSubmissionConditionsValidator.php b/src/WebformSubmissionConditionsValidator.php index 0ef12abc2..2ded745f8 100644 --- a/src/WebformSubmissionConditionsValidator.php +++ b/src/WebformSubmissionConditionsValidator.php @@ -209,6 +209,132 @@ class WebformSubmissionConditionsValidator implements WebformSubmissionCondition unset($element['#states']); } } + + // Loop through visible elements with composite elements with #states. + foreach ($visible_elements as &$element) { + if (empty($element['#webform_composite'])) { + continue; + } + foreach (array_keys($element['#webform_composite_elements']) as $composite_element_key) { + if (isset($element['#' . $composite_element_key . '__states'])) { + $states = &$element['#' . $composite_element_key . '__states']; + } + else { + $states = []; + } + // Store original #states in #_webform_states. + $element['#' . $composite_element_key . '__webform_states'] = $states; + foreach ($states as $original_state => $conditions) { + if (!is_array($conditions)) { + continue; + } + + // Process state/negate. + [$state, $negate] = $this->processState($original_state); + + // If hide/show we need to make sure that validation is not triggered. + if (strpos($state, 'visible') === 0) { + // @TODO: Is this the correct way to get an after build here? + $element[$composite_element_key]['#after_build'][] = [ + get_class($this), + 'elementAfterBuild' + ]; + } + + $targets = $this->getConditionTargetsVisibility($conditions, $visible_elements); + + // Determine if targets are visible or cross page. + $all_targets_visible = (array_sum($targets) === count($targets)); + $has_cross_page_targets = (!$all_targets_visible && array_sum($targets)); + + // Skip if evaluating conditions when all targets are visible. + if ($all_targets_visible) { + // Add .js-webform-states-hidden to element's that are not visible when + // the form is rendered. + if (strpos($state, 'visible') === 0 + && !$this->validateConditions($conditions, $webform_submission)) { + // @TODO: I don't know if/how we can do this. + // $this->addStatesHiddenToElement($element); + } + continue; + } + + // Replace hidden cross page targets with hidden inputs. + if ($has_cross_page_targets) { + $cross_page_targets = array_filter( + $targets, + function($visible) { + return $visible === FALSE; + } + ); + $states[$original_state] = $this->replaceCrossPageTargets($conditions, $webform_submission, $cross_page_targets, $form); + continue; + } + + $result = $this->validateConditions($conditions, $webform_submission); + + // Skip invalid conditions. + if ($result === NULL) { + continue; + } + + // Negate the result. + $result = ($negate) ? !$result : $result; + + // Apply result to element state. + switch ($state) { + case 'required': + $element['#' . $composite_element_key . '__required'] = $result; + break; + + case 'readonly': + + // Set custom readonly attribute and class. + // We can't use the custom #readonly property because it is + // processed before cross page targets. + // @see \Drupal\webform\Plugin\WebformElementBase::prepare + if ($result) { + $element['#' . $composite_element_key . '__attributes']['readonly'] = 'readonly'; + $element['#' . $composite_element_key . '__wrapper_attributes']['class'][] = 'webform-readonly'; + } + break; + + case 'disabled': + $element['#' . $composite_element_key . '__disabled'] = $result; + break; + + case 'visible': + case 'visible-slide': + if (!$result) { + // Visual hide the element. + // @TODO: I don't know if/how we can do this. + // $this->addStatesHiddenToElement($element); + // Clear the default value. + if (!isset($element['#' . $composite_element_key . '__states_clear']) || $element['#' . $composite_element_key . '__states_clear'] === TRUE) { + unset($element['#' . $composite_element_key . '__default_value']); + } + } + break; + + case 'collapsed': + $element['#' . $composite_element_key . '__open'] = !$result; + break; + + case 'checked': + $element['#' . $composite_element_key . '__default_value'] = $result; + break; + } + + // Remove #states state/conditions. + unset($states[$original_state]); + } + + // Remove #states if all states have been applied. + if (empty($states)) { + unset($element['#' . $composite_element_key . '__states']); + } + } + } } /** -- GitLab From 2e68b5d55379b909d4e1291449f281f1db912b03 Mon Sep 17 00:00:00 2001 From: Steven Jones <steven.jones@computerminds.co.uk> Date: Wed, 12 Mar 2025 16:12:01 +0000 Subject: [PATCH 2/2] Issue #3197504 by steven jones: [#States API] Address an issue with the new code. --- src/WebformSubmissionConditionsValidator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WebformSubmissionConditionsValidator.php b/src/WebformSubmissionConditionsValidator.php index 2ded745f8..a35dad3c7 100644 --- a/src/WebformSubmissionConditionsValidator.php +++ b/src/WebformSubmissionConditionsValidator.php @@ -212,7 +212,7 @@ class WebformSubmissionConditionsValidator implements WebformSubmissionCondition // Loop through visible elements with composite elements with #states. foreach ($visible_elements as &$element) { - if (empty($element['#webform_composite'])) { + if (empty($element['#webform_composite']) || empty($element['#webform_composite_elements'])) { continue; } foreach (array_keys($element['#webform_composite_elements']) as $composite_element_key) { -- GitLab