From a94f0e35aa9da788efc2c55a95da34888ee2c90e Mon Sep 17 00:00:00 2001 From: Franco Springveldt Date: Fri, 28 Apr 2017 10:22:15 +1200 Subject: [PATCH] Enhancement: Implemented and/or display rules for UserForms --- code/model/EditableCustomRule.php | 93 +++++++- code/model/UserDefinedForm.php | 224 +++++------------- .../editableformfields/EditableCheckbox.php | 6 + .../EditableCheckboxGroupField.php | 16 ++ .../editableformfields/EditableFormField.php | 217 +++++++++++++---- .../editableformfields/EditableRadioField.php | 4 + .../editableformfields/EditableTextField.php | 2 + tests/EditableCustomRuleTest.php | 31 +++ tests/EditableCustomRuleTest.yml | 30 +++ tests/EditableFormFieldTest.php | 9 + tests/EditableFormFieldTest.yml | 20 +- 11 files changed, 439 insertions(+), 213 deletions(-) create mode 100644 tests/EditableCustomRuleTest.php create mode 100644 tests/EditableCustomRuleTest.yml diff --git a/code/model/EditableCustomRule.php b/code/model/EditableCustomRule.php index 8ee1a3a..da1553c 100644 --- a/code/model/EditableCustomRule.php +++ b/code/model/EditableCustomRule.php @@ -6,6 +6,10 @@ * * @method EditableFormField Parent() * @package userforms + * + * @property string Display + * @property string ConditionOption + * @property string FieldValue */ class EditableCustomRule extends DataObject { @@ -147,4 +151,91 @@ class EditableCustomRule extends DataObject { return $this->canDelete($member); } -} + + /** + * Substitutes configured rule logic with it's JS equivalents and returns them as array elements + * @return array + */ + public function buildExpression() + { + /** @var EditableFormField $formFieldWatch */ + $formFieldWatch = $this->ConditionField(); + //Encapsulated the action to the object + $action = $formFieldWatch->getJsEventHandler(); + + // is this field a special option field + $checkboxField = $formFieldWatch->isCheckBoxField(); + $radioField = $formFieldWatch->isRadioField(); + $target = sprintf('$("%s")', $formFieldWatch->getSelectorFieldOnly()); + $fieldValue = Convert::raw2js($this->FieldValue); + + $conditionOptions = array( + 'ValueLessThan' => '<', + 'ValueLessThanEqual' => '<=', + 'ValueGreaterThan' => '>', + 'ValueGreaterThanEqual' => '>=' + ); + // and what should we evaluate + switch ($this->ConditionOption) { + case 'IsNotBlank': + case 'IsBlank': + $expression = ($checkboxField || $radioField) ? "!{$target}.is(\":checked\")" : "{$target}.val() == ''"; + if ($this->ConditionOption == 'IsNotBlank') { + //Negate + $expression = "!({$expression})"; + } + break; + case 'HasValue': + case 'ValueNot': + if ($checkboxField) { + if ($formFieldWatch->isCheckBoxGroupField()) { + $expression = sprintf("$.inArray('%s', %s.filter(':checked').map(function(){ return $(this).val();}).get()) > -1", + $fieldValue, $target); + } else { + $expression = "{$target}.prop('checked')"; + } + } elseif ($radioField) { + // We cannot simply get the value of the radio group, we need to find the checked option first. + $expression = sprintf('%s.closest(".field, .control-group").find("input:checked").val() == "%s"', + $target, $fieldValue); + } else { + $expression = sprintf('%s.val() == "%s"', $target, $fieldValue); + } + + if ($this->ConditionOption == 'ValueNot') { + //Negate + $expression = "!({$expression})"; + } + break; + case 'ValueLessThan': + case 'ValueLessThanEqual': + case 'ValueGreaterThan': + case 'ValueGreaterThanEqual': + $expression = sprintf('%s.val() %s parseFloat("%s")', $target, + $conditionOptions[$this->ConditionOption], $fieldValue); + break; + default: + throw new LogicException("Unhandled rule {$this->ConditionOption}"); + break; + } + + $result = array( + 'operation' => $expression, + 'event' => $action, + ); + + return $result; + } + + /** + * Returns the opposite of the show/hide pairs of strings + * + * @param string $text + * + * @return string + */ + public function toggleDisplayText($text) + { + return (strtolower($text) === 'show') ? 'hide' : 'show'; + } +} \ No newline at end of file diff --git a/code/model/UserDefinedForm.php b/code/model/UserDefinedForm.php index 2663abb..09e76fe 100755 --- a/code/model/UserDefinedForm.php +++ b/code/model/UserDefinedForm.php @@ -6,7 +6,7 @@ class UserDefinedForm extends Page { - + /** * @var string */ @@ -454,183 +454,29 @@ class UserDefinedForm_Controller extends Page_Controller $rules = ""; $watch = array(); - $watchLoad = array(); if ($this->Fields()) { + /** @var EditableFormField $field */ foreach ($this->Fields() as $field) { - $holderSelector = $field->getSelectorHolder(); - - // Is this Field Show by Default - if (!$field->ShowOnLoad) { - $default .= "{$holderSelector}.hide().trigger('userform.field.hide');\n"; - } - - // Check for field dependencies / default - foreach ($field->EffectiveDisplayRules() as $rule) { - - // Get the field which is effected - $formFieldWatch = EditableFormField::get()->byId($rule->ConditionFieldID); - - // Skip deleted fields - if (!$formFieldWatch) { - continue; - } - - $fieldToWatch = $formFieldWatch->getSelectorField($rule); - $fieldToWatchOnLoad = $formFieldWatch->getSelectorField($rule, true); - - // show or hide? - $view = ($rule->Display == 'Hide') ? 'hide' : 'show'; - $opposite = ($view == "show") ? "hide" : "show"; - - // what action do we need to keep track of. Something nicer here maybe? - // @todo encapulsation - $action = "change"; - - if ($formFieldWatch instanceof EditableTextField) { - $action = "keyup"; - } - - // is this field a special option field - $checkboxField = false; - $radioField = false; - - if (in_array($formFieldWatch->ClassName, array('EditableCheckboxGroupField', 'EditableCheckbox'))) { - $action = "click"; - $checkboxField = true; - } elseif ($formFieldWatch->ClassName == "EditableRadioField") { - $radioField = true; - } - - // and what should we evaluate - switch ($rule->ConditionOption) { - case 'IsNotBlank': - $expression = ($checkboxField || $radioField) ? '$(this).is(":checked")' :'$(this).val() != ""'; - - break; - case 'IsBlank': - $expression = ($checkboxField || $radioField) ? '!($(this).is(":checked"))' : '$(this).val() == ""'; - - break; - case 'HasValue': - if ($checkboxField) { - $expression = '$(this).prop("checked")'; - } elseif ($radioField) { - // We cannot simply get the value of the radio group, we need to find the checked option first. - $expression = '$(this).parents(".field, .control-group").find("input:checked").val()=="'. $rule->FieldValue .'"'; - } else { - $expression = '$(this).val() == "'. $rule->FieldValue .'"'; - } - - break; - case 'ValueLessThan': - $expression = '$(this).val() < parseFloat("'. $rule->FieldValue .'")'; - - break; - case 'ValueLessThanEqual': - $expression = '$(this).val() <= parseFloat("'. $rule->FieldValue .'")'; - - break; - case 'ValueGreaterThan': - $expression = '$(this).val() > parseFloat("'. $rule->FieldValue .'")'; - - break; - case 'ValueGreaterThanEqual': - $expression = '$(this).val() >= parseFloat("'. $rule->FieldValue .'")'; - - break; - default: // ==HasNotValue - if ($checkboxField) { - $expression = '!$(this).prop("checked")'; - } elseif ($radioField) { - // We cannot simply get the value of the radio group, we need to find the checked option first. - $expression = '$(this).parents(".field, .control-group").find("input:checked").val()!="'. $rule->FieldValue .'"'; - } else { - $expression = '$(this).val() != "'. $rule->FieldValue .'"'; - } - - break; - } - - if (!isset($watch[$fieldToWatch])) { - $watch[$fieldToWatch] = array(); - } - - $watch[$fieldToWatch][] = array( - 'expression' => $expression, - 'holder_selector' => $holderSelector, - 'view' => $view, - 'opposite' => $opposite, - 'action' => $action - ); - - $watchLoad[$fieldToWatchOnLoad] = true; + if ($result = $field->formatDisplayRules()) { + $watch[] = $result; } } } - if ($watch) { - foreach ($watch as $key => $values) { - $logic = array(); - $actions = array(); - - foreach ($values as $rule) { - // Assign action - $actions[$rule['action']] = $rule['action']; - - // Assign behaviour - $expression = $rule['expression']; - $holder = $rule['holder_selector']; - $view = $rule['view']; // hide or show - $opposite = $rule['opposite']; - // Generated javascript for triggering visibility - $logic[] = <<<"EOS" -if({$expression}) { - {$holder} - .{$view}() - .trigger('userform.field.{$view}'); -} else { - {$holder} - .{$opposite}() - .trigger('userform.field.{$opposite}'); -} -EOS; - } - - $logic = implode("\n", $logic); - $rules .= $key.".each(function() {\n - $(this).data('userformConditions', function() {\n - $logic\n - }); \n -});\n"; - foreach ($actions as $action) { - $rules .= $key.".$action(function() { - $(this).data('userformConditions').call(this);\n -});\n"; - } - } - } - - if ($watchLoad) { - foreach ($watchLoad as $key => $value) { - $rules .= $key.".each(function() { - $(this).data('userformConditions').call(this);\n -});\n"; - } + $rules .= $this->buildWatchJS($watch); } // Only add customScript if $default or $rules is defined - if ($default || $rules) { + if ($rules) { Requirements::customScript(<<getMergeFieldsMap($emailData['Fields']); - + if ($attachments) { foreach ($attachments as $file) { if ($file->ID != 0) { @@ -739,7 +585,7 @@ JS } } } - + $parsedBody = SSViewer::execute_string($recipient->getEmailBodyContent(), $mergeFields); if (!$recipient->SendPlain && $recipient->emailTemplateExists()) { @@ -897,4 +743,46 @@ JS 'Form' => '', )); } + + /** + * Outputs the required JS from the $watch input + * + * @param array $watch + * + * @return string + */ + protected function buildWatchJS($watch) + { + $result = ''; + foreach ($watch as $key => $rule) { + $events = implode(' ', $rule['events']); + $selectors = implode(', ', $rule['selectors']); + $conjunction = $rule['conjunction']; + $operations = implode(" {$conjunction} ", $rule['operations']); + $target = $rule['targetFieldID']; + $initialState = $rule['initialState']; + $view = $rule['view']; + $opposite = $rule['opposite']; + + $result .= << 'Boolean' // from CustomSettings ); @@ -61,4 +63,8 @@ class EditableCheckbox extends EditableFormField parent::migrateSettings($data); } + + public function isCheckBoxField() { + return true; + } } diff --git a/code/model/editableformfields/EditableCheckboxGroupField.php b/code/model/editableformfields/EditableCheckboxGroupField.php index f98faf3..bc02fc1 100755 --- a/code/model/editableformfields/EditableCheckboxGroupField.php +++ b/code/model/editableformfields/EditableCheckboxGroupField.php @@ -14,6 +14,8 @@ class EditableCheckboxGroupField extends EditableMultipleOptionField private static $plural_name = "Checkbox Groups"; + protected $jsEventHandler = 'click'; + public function getFormField() { $field = new UserFormsCheckboxSetField($this->Name, $this->EscapedTitle, $this->getOptionsMap()); @@ -59,4 +61,18 @@ class EditableCheckboxGroupField extends EditableMultipleOptionField return "$(\"input[name='{$this->Name}[]']:first\")"; } } + + public function isCheckBoxField() { + return true; + } + + public function getSelectorFieldOnly() + { + return "[name='{$this->Name}[]']"; + } + + public function isCheckBoxGroupField() + { + return true; + } } diff --git a/code/model/editableformfields/EditableFormField.php b/code/model/editableformfields/EditableFormField.php index c4f4078..45ac497 100755 --- a/code/model/editableformfields/EditableFormField.php +++ b/code/model/editableformfields/EditableFormField.php @@ -14,6 +14,8 @@ use SilverStripe\Forms\SegmentField; * @property int $Sort * @property bool $Required * @property string $CustomErrorMessage + * @property boolean $ShowOnLoad + * @property string $DisplayRulesConjunction * @method UserDefinedForm Parent() Parent page * @method DataList DisplayRules() List of EditableCustomRule objects */ @@ -87,8 +89,10 @@ class EditableFormField extends DataObject "RightTitle" => "Varchar(255)", // from CustomSettings "ShowOnLoad" => "Boolean(1)", // from CustomSettings "ShowInSummary" => "Boolean", + 'DisplayRulesConjunction' => 'Enum("And,Or","Or")', ); + private static $defaults = array( 'ShowOnLoad' => true, ); @@ -125,6 +129,22 @@ class EditableFormField extends DataObject */ protected $readonly; + /** + * Property holds the JS event which gets fired for this type of element + * + * @var string + */ + protected $jsEventHandler = 'change'; + + /** + * Returns the jsEventHandler property for the current object. Bearing in mind it could've been overridden. + * @return string + */ + public function getJsEventHandler() + { + return $this->jsEventHandler; + } + /** * Set the visibility of an individual form field * @@ -244,44 +264,36 @@ class EditableFormField extends DataObject // Check display rules if ($this->Required) { return new FieldList( - LabelField::create(_t( - 'EditableFormField.DISPLAY_RULES_DISABLED', - 'Display rules are not enabled for required fields. ' . - 'Please uncheck "Is this field Required?" under "Validation" to re-enable.' - ))->addExtraClass('message warning') - ); + LabelField::create( + _t( + 'EditableFormField.DISPLAY_RULES_DISABLED', + 'Display rules are not enabled for required fields. Please uncheck "Is this field Required?" under "Validation" to re-enable.')) + ->addExtraClass('message warning')); } $self = $this; $allowedClasses = array_keys($this->getEditableFieldClasses(false)); $editableColumns = new GridFieldEditableColumns(); $editableColumns->setDisplayFields(array( - 'Display' => '', - 'ConditionFieldID' => function ($record, $column, $grid) use ($allowedClasses, $self) { - return DropdownField::create( - $column, - '', - EditableFormField::get() - ->filter(array( - 'ParentID' => $self->ParentID, - 'ClassName' => $allowedClasses - )) - ->exclude(array( - 'ID' => $self->ID - )) - ->map('ID', 'Title') - ); - }, - 'ConditionOption' => function ($record, $column, $grid) { - $options = Config::inst()->get('EditableCustomRule', 'condition_options'); - return DropdownField::create($column, '', $options); - }, - 'FieldValue' => function ($record, $column, $grid) { - return TextField::create($column); - }, - 'ParentID' => function ($record, $column, $grid) use ($self) { - return HiddenField::create($column, '', $self->ID); - } - )); + 'ConditionFieldID' => function ($record, $column, $grid) use ($allowedClasses, $self) { + return DropdownField::create($column, '', EditableFormField::get()->filter(array( + 'ParentID' => $self->ParentID, + 'ClassName' => $allowedClasses, + ))->exclude(array( + 'ID' => $self->ID, + ))->map('ID', 'Title')); + }, + 'ConditionOption' => function ($record, $column, $grid) { + $options = Config::inst()->get('EditableCustomRule', 'condition_options'); + + return DropdownField::create($column, '', $options); + }, + 'FieldValue' => function ($record, $column, $grid) { + return TextField::create($column); + }, + 'ParentID' => function ($record, $column, $grid) use ($self) { + return HiddenField::create($column, '', $self->ID); + }, + )); // Custom rules $customRulesConfig = GridFieldConfig::create() @@ -294,11 +306,20 @@ class EditableFormField extends DataObject ); return new FieldList( - CheckboxField::create('ShowOnLoad') - ->setDescription(_t( - 'EditableFormField.SHOWONLOAD', - 'Initial visibility before processing these rules' - )), + DropdownField::create('ShowOnLoad', + _t('EditableFormField.INITIALVISIBILITY', 'Initial visibility'), + array( + 1 => 'Show', + 0 => 'Hide', + ) + ), + DropdownField::create('DisplayRulesConjunction', + _t('EditableFormField.DISPLAYIF', 'Toggle visibility when'), + array( + 'Or' => _t('UserDefinedForm.SENDIFOR', 'Any conditions are true'), + 'And' => _t('UserDefinedForm.SENDIFAND', 'All conditions are true'), + ) + ), GridField::create( 'DisplayRules', _t('EditableFormField.CUSTOMRULES', 'Custom Rules'), @@ -489,6 +510,7 @@ class EditableFormField extends DataObject { $this->publish($fromStage, $toStage, $createNewVersion); + $seenIDs = array(); // Don't forget to publish the related custom rules... @@ -899,18 +921,38 @@ class EditableFormField extends DataObject */ public function getSelectorHolder() { - return "$(\"#{$this->Name}\")"; + return sprintf('$("%s")', $this->getSelectorOnly()); + } + + /** + * Returns only the JS identifier of a string, less the $(), which can be inserted elsewhere, for example when you + * want to perform selections on multiple selectors + * @return string + */ + public function getSelectorOnly() + { + return "#{$this->Name}"; } /** * Gets the JS expression for selecting the value for this field * - * @param EditableCustomRule $rule Custom rule this selector will be used with - * @param bool $forOnLoad Set to true if this will be invoked on load + * @param EditableCustomRule $rule Custom rule this selector will be used with + * @param bool $forOnLoad Set to true if this will be invoked on load + * + * @return string */ public function getSelectorField(EditableCustomRule $rule, $forOnLoad = false) { - return "$(\"input[name='{$this->Name}']\")"; + return sprintf("$(%s)", $this->getSelectorFieldOnly()); + } + + /** + * @return string + */ + public function getSelectorFieldOnly() + { + return "[name='{$this->Name}']"; } @@ -970,4 +1012,95 @@ class EditableFormField extends DataObject } return $this->DisplayRules(); } + + /** + * Extracts info from DisplayRules into array so UserDefinedForm->buildWatchJS can run through it. + * @return array|null + */ + public function formatDisplayRules() + { + $holderSelector = $this->getSelectorOnly(); + $result = array( + 'targetFieldID' => $holderSelector, + 'conjunction' => $this->DisplayRulesConjunctionNice(), + 'selectors' => array(), + 'events' => array(), + 'operations' => array(), + 'initialState' => $this->ShowOnLoadNice(), + 'view' => array(), + 'opposite' => array(), + ); + // Check for field dependencies / default + /** @var EditableCustomRule $rule */ + foreach ($this->EffectiveDisplayRules() as $rule) { + // Get the field which is effected + /** @var EditableFormField $formFieldWatch */ + $formFieldWatch = EditableFormField::get()->byId($rule->ConditionFieldID); + // Skip deleted fields + if (! $formFieldWatch) { + continue; + } + $fieldToWatch = $formFieldWatch->getSelectorFieldOnly(); + + $expression = $rule->buildExpression(); + if (! in_array($fieldToWatch, $result['selectors'])) { + $result['selectors'][] = $fieldToWatch; + } + if (! in_array($expression['event'], $result['events'])) { + $result['events'][] = $expression['event']; + } + $result['operations'][] = $expression['operation']; + + //View/Show should read + $result['view'] = $rule->toggleDisplayText($result['initialState']); + $result['opposite'] = $rule->toggleDisplayText($result['view']); + } + + return (count($result['selectors'])) ? $result : null; + } + + /** + * Replaces the set DisplayRulesConjunction with their JS logical operators + * @return string + */ + public function DisplayRulesConjunctionNice() + { + return (strtolower($this->DisplayRulesConjunction) === 'or') ? '||' : '&&'; + } + + /** + * Replaces boolean ShowOnLoad with its JS string equivalent + * @return string + */ + public function ShowOnLoadNice() + { + return ($this->ShowOnLoad) ? 'show' : 'hide'; + } + + /** + * Returns whether this is of type EditableCheckBoxField + * @return bool + */ + public function isCheckBoxField() + { + return false; + } + + /** + * Returns whether this is of type EditableRadioField + * @return bool + */ + public function isRadioField() + { + return false; + } + + /** + * Determined is this is of type EditableCheckboxGroupField + * @return bool + */ + public function isCheckBoxGroupField() + { + return false; + } } diff --git a/code/model/editableformfields/EditableRadioField.php b/code/model/editableformfields/EditableRadioField.php index 745c757..1c4f5fd 100755 --- a/code/model/editableformfields/EditableRadioField.php +++ b/code/model/editableformfields/EditableRadioField.php @@ -46,4 +46,8 @@ class EditableRadioField extends EditableMultipleOptionField $first = $forOnLoad ? ':first' : ''; return "$(\"input[name='{$this->Name}']{$first}\")"; } + + public function isRadioField() { + return true; + } } diff --git a/code/model/editableformfields/EditableTextField.php b/code/model/editableformfields/EditableTextField.php index 05d8e11..4983a64 100755 --- a/code/model/editableformfields/EditableTextField.php +++ b/code/model/editableformfields/EditableTextField.php @@ -44,6 +44,8 @@ class EditableTextField extends EditableFormField 'url' => 'Home page' ); + protected $jsEventHandler = 'keyup'; + private static $db = array( 'MinLength' => 'Int', 'MaxLength' => 'Int', diff --git a/tests/EditableCustomRuleTest.php b/tests/EditableCustomRuleTest.php new file mode 100644 index 0000000..6429e33 --- /dev/null +++ b/tests/EditableCustomRuleTest.php @@ -0,0 +1,31 @@ +objFromFixture('EditableCustomRule', 'rule1'); + $result1 = $rule1->buildExpression(); + + //Dropdowns expect change event + $this->assertEquals('change', $result1['event']); + $this->assertNotEmpty($result1['operation']); + //Check for equals sign + $this->assertContains('==', $result1['operation']); + + /** @var EditableCustomRule $rule2 */ + $rule2 = $this->objFromFixture('EditableCustomRule', 'rule2'); + $result2 = $rule2->buildExpression(); + //TextField expect change event + $this->assertEquals('keyup', $result2['event']); + $this->assertNotEmpty($result2['operation']); + //Check for greater than sign + $this->assertContains('>', $result2['operation']); + } +} \ No newline at end of file diff --git a/tests/EditableCustomRuleTest.yml b/tests/EditableCustomRuleTest.yml new file mode 100644 index 0000000..33edc27 --- /dev/null +++ b/tests/EditableCustomRuleTest.yml @@ -0,0 +1,30 @@ +EditableFormField: + countryDropdown: + ClassName: EditableCountryDropdownField + Name: CountrySelection + Title: "Choose your country" + DisplayRulesConjunction: And + ShowOnLoad: false + irdNumberField: + ClassName: EditableTextField + Name: IRDNumber + Title: "Enter your IRD Number" + countryTextField: + ClassName: EditableTextField + Name: CountryTextSelection + Title: "Enter your country (2 digit prefix)" + DisplayRulesConjunction: And + ShowOnLoad: false +EditableCustomRule: + rule1: + Display: Show + ConditionOption: HasValue + FieldValue: NZ + ConditionField: =>EditableFormField.countryDropdown + Parent: =>EditableFormField.irdNumberField + rule2: + Display: Show + ConditionOption: ValueGreaterThan + FieldValue: 1 + ConditionField: =>EditableFormField.countryTextField + Parent: =>EditableFormField.irdNumberField diff --git a/tests/EditableFormFieldTest.php b/tests/EditableFormFieldTest.php index 79981fe..5f6b676 100644 --- a/tests/EditableFormFieldTest.php +++ b/tests/EditableFormFieldTest.php @@ -204,4 +204,13 @@ class EditableFormFieldTest extends FunctionalTest $this->assertEquals(10, $attributes['data-rule-minlength']); $this->assertEquals(20, $attributes['data-rule-maxlength']); } + + public function testFormatDisplayRules() + { + /** @var EditableCheckbox $checkbox */ + $checkbox = $this->objFromFixture('EditableFormField', 'irdNumberField'); + $displayRules = $checkbox->formatDisplayRules(); + $this->assertNotNull($displayRules); + $this->assertCount(1, $displayRules['operations']); + } } diff --git a/tests/EditableFormFieldTest.yml b/tests/EditableFormFieldTest.yml index e4f308f..6e99842 100644 --- a/tests/EditableFormFieldTest.yml +++ b/tests/EditableFormFieldTest.yml @@ -1,9 +1,25 @@ +EditableFormField: + irdNumberField: + ClassName: EditableTextField + Name: IRDNumber + Title: "Enter your IRD Number" + countryTextField: + ClassName: EditableTextField + Name: CountryTextSelection + Title: "Enter your country (2 digit prefix)" + DisplayRulesConjunction: And + ShowOnLoad: false EditableCustomRule: + rule1: + Display: Show + ConditionOption: HasValue + FieldValue: NZ + ConditionField: =>EditableFormField.countryTextField + Parent: =>EditableFormField.irdNumberField rule-1: Display: Hide ConditionOption: HasValue FieldValue: 6 - EditableOption: option-1: Name: Option1 @@ -144,4 +160,4 @@ UserDefinedForm: Title: Custom Rules Form Fields: =>EditableCheckbox.checkbox-with-rule, =>EditableTextField.basic-text-2 empty-form: - Title: Empty Form + Title: Empty Form \ No newline at end of file