Enhance form logic property resolver and validation UI

- Refactor `shouldBeRequired()` method in FormLogicPropertyResolver for clearer logic and better handling of conditional requirements
- Add comprehensive test cases for dynamic field requirement logic
- Update CustomFieldValidation component with improved UI and guidance for validation setup
- Improve error message and validation rule description in form validation interface
This commit is contained in:
Julien Nahum 2025-02-11 15:05:27 +01:00
parent f5b9b86c16
commit 853760484c
3 changed files with 142 additions and 31 deletions

View File

@ -29,22 +29,28 @@ class FormLogicPropertyResolver
public function shouldBeRequired(): bool public function shouldBeRequired(): bool
{ {
if (! isset($this->property['required'])) { // Default required to false if not set
return false; $isRequired = $this->property['required'] ?? false;
}
if (! $this->logic) { if (! $this->logic) {
return $this->property['required']; return $isRequired;
} }
$conditionsMet = FormLogicConditionChecker::conditionsMet($this->logic['conditions'], $this->formData); $conditionsMet = FormLogicConditionChecker::conditionsMet($this->logic['conditions'], $this->formData);
if ($conditionsMet && $this->property['required'] && count($this->logic['actions']) > 0 && (in_array('make-it-optional', $this->logic['actions']) || in_array('hide-block', $this->logic['actions']))) {
return false; // If conditions are met and we have actions
} elseif ($conditionsMet && ! $this->property['required'] && count($this->logic['actions']) > 0 && in_array('require-answer', $this->logic['actions'])) { if ($conditionsMet && !empty($this->logic['actions'])) {
return true; // If field is required but should be made optional
} else { if ($isRequired && (in_array('make-it-optional', $this->logic['actions']) || in_array('hide-block', $this->logic['actions']))) {
return $this->property['required']; return false;
}
// If field is not required but should be required
if (!$isRequired && in_array('require-answer', $this->logic['actions'])) {
return true;
}
} }
return $isRequired;
} }
public function shouldBeHidden(): bool public function shouldBeHidden(): bool

View File

@ -107,4 +107,91 @@ it('can validate form logic property resolver', function ($property, $formData,
['93ea3198-353f-440b-8dc9-2ac9a7bee124' => [], '93ea3198-353f-440b-8dc9-2ac9a7bee222' => ['abc']], ['93ea3198-353f-440b-8dc9-2ac9a7bee124' => [], '93ea3198-353f-440b-8dc9-2ac9a7bee222' => ['abc']],
false, false,
], ],
[
[
'id' => 'text_field',
'name' => 'Required if checked',
'type' => 'text',
'hidden' => false,
'logic' => [
'conditions' => [
'operatorIdentifier' => 'and',
'children' => [
[
'identifier' => 'checkbox',
'value' => [
'operator' => 'is_checked',
'property_meta' => [
'id' => 'checkbox_field',
'type' => 'checkbox'
],
'value' => true
]
]
]
],
'actions' => ['require-answer']
]
],
['checkbox_field' => true],
true
],
[
[
'id' => 'text_field',
'name' => 'Required if checked',
'type' => 'text',
'hidden' => false,
'logic' => [
'conditions' => [
'operatorIdentifier' => 'and',
'children' => [
[
'identifier' => 'checkbox',
'value' => [
'operator' => 'is_checked',
'property_meta' => [
'id' => 'checkbox_field',
'type' => 'checkbox'
],
'value' => true
]
]
]
],
'actions' => ['require-answer']
]
],
['checkbox_field' => false],
false
],
[
[
'id' => 'text_field',
'name' => 'Required if checked',
'type' => 'text',
'hidden' => false,
'logic' => [
'conditions' => [
'operatorIdentifier' => 'and',
'children' => [
[
'identifier' => 'checkbox',
'value' => [
'operator' => 'is_checked',
'property_meta' => [
'id' => 'checkbox_field',
'type' => 'checkbox'
],
'value' => true
]
]
]
],
'actions' => ['require-answer']
]
],
['checkbox_field' => null],
false
]
]); ]);

View File

@ -1,33 +1,51 @@
<template> <template>
<div class="py-2 px-4"> <div class="py-2 px-4">
<p class="text-gray-500 text-xs mb-3"> <div class="space-y-4">
Add some custom validation. Save your form before testing. <!-- Step 1: Validation Rules -->
</p> <div>
<h3 class="font-medium text-gray-900 text-sm mb-1">
<div class="py-2"> Step 1: Set Validation Rules
<p class="font-semibold text-sm text-gray-700"> </h3>
Validation criteria for field acceptance <p class="text-gray-500 text-xs mb-3">
</p> Define <span class="font-semibold">conditions that must be met</span> for this field to be valid. If these conditions are not met, the field will be marked as invalid.
<condition-editor </p>
ref="filter-editor" <condition-editor
v-model="validation.conditions" ref="filter-editor"
class="mt-1 border-t border rounded-md mb-3" v-model="validation.conditions"
:form="form" class="mt-1 border-t border rounded-md"
/> :form="form"
<text-input />
name="error_message" </div>
class=""
:form="field.validation" <!-- Step 2: Error Message -->
label="Error message" <div>
help="Displayed when the validation fails" <h3 class="font-medium text-gray-900 text-sm mb-1">
Step 2: Set Error Message
</h3>
<p class="text-gray-500 text-xs mb-2">
Enter the message that will be <span class="font-semibold">shown to users when the validation rules above are not met</span>.
</p>
<text-input
name="error_message"
:form="field.validation"
label="Error message"
/>
</div>
<UAlert
icon="i-heroicons-information-circle"
color="yellow"
variant="subtle"
size="sm"
description="Remember to save your form to apply these validation rules."
/> />
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import ConditionEditor from "./form-logic-components/ConditionEditor.client.vue"
import { default as _has } from "lodash/has" import { default as _has } from "lodash/has"
import ConditionEditor from "./form-logic-components/ConditionEditor.client.vue"
export default { export default {
name: 'FormValidation', name: 'FormValidation',
components: {ConditionEditor}, components: {ConditionEditor},