Skip to content

Commit 8537604

Browse files
committed
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
1 parent f5b9b86 commit 8537604

File tree

3 files changed

+142
-31
lines changed

3 files changed

+142
-31
lines changed

api/app/Service/Forms/FormLogicPropertyResolver.php

+16-10
Original file line numberDiff line numberDiff line change
@@ -29,22 +29,28 @@ public static function isHidden(array $property, array $values): bool
2929

3030
public function shouldBeRequired(): bool
3131
{
32-
if (! isset($this->property['required'])) {
33-
return false;
34-
}
32+
// Default required to false if not set
33+
$isRequired = $this->property['required'] ?? false;
3534

3635
if (! $this->logic) {
37-
return $this->property['required'];
36+
return $isRequired;
3837
}
3938

4039
$conditionsMet = FormLogicConditionChecker::conditionsMet($this->logic['conditions'], $this->formData);
41-
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']))) {
42-
return false;
43-
} elseif ($conditionsMet && ! $this->property['required'] && count($this->logic['actions']) > 0 && in_array('require-answer', $this->logic['actions'])) {
44-
return true;
45-
} else {
46-
return $this->property['required'];
40+
41+
// If conditions are met and we have actions
42+
if ($conditionsMet && !empty($this->logic['actions'])) {
43+
// If field is required but should be made optional
44+
if ($isRequired && (in_array('make-it-optional', $this->logic['actions']) || in_array('hide-block', $this->logic['actions']))) {
45+
return false;
46+
}
47+
// If field is not required but should be required
48+
if (!$isRequired && in_array('require-answer', $this->logic['actions'])) {
49+
return true;
50+
}
4751
}
52+
53+
return $isRequired;
4854
}
4955

5056
public function shouldBeHidden(): bool

api/tests/Unit/Service/Forms/FormLogicPropertyResolverTest.php

+87
Original file line numberDiff line numberDiff line change
@@ -107,4 +107,91 @@
107107
['93ea3198-353f-440b-8dc9-2ac9a7bee124' => [], '93ea3198-353f-440b-8dc9-2ac9a7bee222' => ['abc']],
108108
false,
109109
],
110+
[
111+
[
112+
'id' => 'text_field',
113+
'name' => 'Required if checked',
114+
'type' => 'text',
115+
'hidden' => false,
116+
'logic' => [
117+
'conditions' => [
118+
'operatorIdentifier' => 'and',
119+
'children' => [
120+
[
121+
'identifier' => 'checkbox',
122+
'value' => [
123+
'operator' => 'is_checked',
124+
'property_meta' => [
125+
'id' => 'checkbox_field',
126+
'type' => 'checkbox'
127+
],
128+
'value' => true
129+
]
130+
]
131+
]
132+
],
133+
'actions' => ['require-answer']
134+
]
135+
],
136+
['checkbox_field' => true],
137+
true
138+
],
139+
[
140+
[
141+
'id' => 'text_field',
142+
'name' => 'Required if checked',
143+
'type' => 'text',
144+
'hidden' => false,
145+
'logic' => [
146+
'conditions' => [
147+
'operatorIdentifier' => 'and',
148+
'children' => [
149+
[
150+
'identifier' => 'checkbox',
151+
'value' => [
152+
'operator' => 'is_checked',
153+
'property_meta' => [
154+
'id' => 'checkbox_field',
155+
'type' => 'checkbox'
156+
],
157+
'value' => true
158+
]
159+
]
160+
]
161+
],
162+
'actions' => ['require-answer']
163+
]
164+
],
165+
['checkbox_field' => false],
166+
false
167+
],
168+
[
169+
[
170+
'id' => 'text_field',
171+
'name' => 'Required if checked',
172+
'type' => 'text',
173+
'hidden' => false,
174+
'logic' => [
175+
'conditions' => [
176+
'operatorIdentifier' => 'and',
177+
'children' => [
178+
[
179+
'identifier' => 'checkbox',
180+
'value' => [
181+
'operator' => 'is_checked',
182+
'property_meta' => [
183+
'id' => 'checkbox_field',
184+
'type' => 'checkbox'
185+
],
186+
'value' => true
187+
]
188+
]
189+
]
190+
],
191+
'actions' => ['require-answer']
192+
]
193+
],
194+
['checkbox_field' => null],
195+
false
196+
]
110197
]);

client/components/open/forms/components/CustomFieldValidation.vue

+39-21
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,51 @@
11
<template>
22
<div class="py-2 px-4">
3-
<p class="text-gray-500 text-xs mb-3">
4-
Add some custom validation. Save your form before testing.
5-
</p>
6-
7-
<div class="py-2">
8-
<p class="font-semibold text-sm text-gray-700">
9-
Validation criteria for field acceptance
10-
</p>
11-
<condition-editor
12-
ref="filter-editor"
13-
v-model="validation.conditions"
14-
class="mt-1 border-t border rounded-md mb-3"
15-
:form="form"
16-
/>
17-
<text-input
18-
name="error_message"
19-
class=""
20-
:form="field.validation"
21-
label="Error message"
22-
help="Displayed when the validation fails"
3+
<div class="space-y-4">
4+
<!-- Step 1: Validation Rules -->
5+
<div>
6+
<h3 class="font-medium text-gray-900 text-sm mb-1">
7+
Step 1: Set Validation Rules
8+
</h3>
9+
<p class="text-gray-500 text-xs mb-3">
10+
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.
11+
</p>
12+
<condition-editor
13+
ref="filter-editor"
14+
v-model="validation.conditions"
15+
class="mt-1 border-t border rounded-md"
16+
:form="form"
17+
/>
18+
</div>
19+
20+
<!-- Step 2: Error Message -->
21+
<div>
22+
<h3 class="font-medium text-gray-900 text-sm mb-1">
23+
Step 2: Set Error Message
24+
</h3>
25+
<p class="text-gray-500 text-xs mb-2">
26+
Enter the message that will be <span class="font-semibold">shown to users when the validation rules above are not met</span>.
27+
</p>
28+
<text-input
29+
name="error_message"
30+
:form="field.validation"
31+
label="Error message"
32+
/>
33+
</div>
34+
35+
<UAlert
36+
icon="i-heroicons-information-circle"
37+
color="yellow"
38+
variant="subtle"
39+
size="sm"
40+
description="Remember to save your form to apply these validation rules."
2341
/>
2442
</div>
2543
</div>
2644
</template>
2745

2846
<script>
29-
import ConditionEditor from "./form-logic-components/ConditionEditor.client.vue"
3047
import { default as _has } from "lodash/has"
48+
import ConditionEditor from "./form-logic-components/ConditionEditor.client.vue"
3149
export default {
3250
name: 'FormValidation',
3351
components: {ConditionEditor},

0 commit comments

Comments
 (0)