Skip to content

Commit a898578

Browse files
committed
PB-2027: simplify the useFieldValidation composable
removing most bi-directional links and having all the "config" in one big catch'em all bag
1 parent 44d0580 commit a898578

File tree

11 files changed

+534
-421
lines changed

11 files changed

+534
-421
lines changed

packages/viewer/src/modules/infobox/components/styling/DrawingStyleMediaLink.vue

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
import { ref } from 'vue'
33
import { useI18n } from 'vue-i18n'
44
5+
import type { ValidationResult } from '@/utils/composables/useFieldValidation'
6+
57
import { MediaType } from '@/modules/infobox/DrawingStyleMediaTypes.enum'
6-
import TextInput, { type TextInputValidateResult } from '@/utils/components/TextInput.vue'
8+
import TextInput from '@/utils/components/TextInput.vue'
79
import { isValidUrl } from '@/utils/utils'
810
911
const { mediaType, urlLabel, descriptionLabel } = defineProps<{
@@ -87,7 +89,7 @@ function validateForm(): boolean {
8789
return isFormValid.value
8890
}
8991
90-
function onUrlValidate(result: TextInputValidateResult): void {
92+
function onUrlValidate(result: ValidationResult): void {
9193
isFormValid.value = result.valid
9294
}
9395
</script>

packages/viewer/src/modules/menu/components/advancedTools/ImportFile/ImportFileLocalTab.vue

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import type { ErrorMessage } from '@swissgeo/log/Message'
44
import log from '@swissgeo/log'
55
import { computed, ref } from 'vue'
66
7+
import type { ValidationResult } from '@/utils/composables/useFieldValidation'
8+
79
import ImportFileButtons from '@/modules/menu/components/advancedTools/ImportFile/ImportFileButtons.vue'
810
import generateErrorMessageFromErrorType from '@/modules/menu/components/advancedTools/ImportFile/parser/errors/generateErrorMessageFromErrorType.utils'
911
import useImportFile from '@/modules/menu/components/advancedTools/ImportFile/useImportFile.composable'
@@ -52,8 +54,8 @@ async function loadFile() {
5254
loadingFile.value = false
5355
}
5456
55-
function validateForm(valid: boolean) {
56-
isFormValid.value = valid
57+
function validateForm(validation: ValidationResult) {
58+
isFormValid.value = validation.valid
5759
}
5860
</script>
5961

@@ -75,7 +77,7 @@ function validateForm(valid: boolean) {
7577
:accepted-file-types="acceptedFileTypes"
7678
:placeholder="'no_file'"
7779
:activate-validation="activateValidation"
78-
:invalid-marker="!!errorFileLoadingMessage"
80+
:force-invalid="!!errorFileLoadingMessage"
7981
:invalid-message="errorFileLoadingMessage?.msg"
8082
:invalid-message-extra-params="errorFileLoadingMessage?.params"
8183
:valid-message="importSuccessMessage"

packages/viewer/src/modules/menu/components/advancedTools/ImportFile/ImportFileOnlineTab.vue

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,13 @@ import { ErrorMessage, WarningMessage } from '@swissgeo/log/Message'
44
import { type ComponentPublicInstance, computed, onMounted, ref, useTemplateRef, watch } from 'vue'
55
66
import type { ActionDispatcher } from '@/store/types'
7+
import type { ValidationResult } from '@/utils/composables/useFieldValidation'
78
89
import ImportFileButtons from '@/modules/menu/components/advancedTools/ImportFile/ImportFileButtons.vue'
910
import generateErrorMessageFromErrorType from '@/modules/menu/components/advancedTools/ImportFile/parser/errors/generateErrorMessageFromErrorType.utils'
1011
import useImportFile from '@/modules/menu/components/advancedTools/ImportFile/useImportFile.composable'
1112
import useUIStore from '@/store/modules/ui'
12-
import TextInput, {
13-
type TextInputExposed,
14-
type TextInputValidateResult,
15-
} from '@/utils/components/TextInput.vue'
13+
import TextInput, { type TextInputExposed } from '@/utils/components/TextInput.vue'
1614
import { isValidUrl } from '@/utils/utils'
1715
1816
const dispatcher: ActionDispatcher = {
@@ -56,7 +54,7 @@ onMounted(() => {
5654
5755
// Methods
5856
59-
function validateUrl(url?: string): TextInputValidateResult {
57+
function validateUrl(url?: string): ValidationResult {
6058
if (!url) {
6159
return { valid: false, invalidMessage: 'no_url' }
6260
} else if (!isValidUrl(url)) {
@@ -70,8 +68,8 @@ function validateForm() {
7068
return isFormValid.value
7169
}
7270
73-
function onUrlValidate(result: TextInputValidateResult) {
74-
isFormValid.value = result.valid
71+
function onUrlValidate(validation: ValidationResult) {
72+
isFormValid.value = validation.valid
7573
}
7674
7775
function onUrlChange() {
@@ -133,7 +131,7 @@ async function loadFile() {
133131
class="mb-2"
134132
placeholder="import_file_url_placeholder"
135133
:activate-validation="activateValidation"
136-
:invalid-marker="!!errorFileLoadingMessage"
134+
:force-invalid="!!errorFileLoadingMessage"
137135
:invalid-message="errorFileLoadingMessage?.msg"
138136
:invalid-message-params="errorFileLoadingMessage?.params"
139137
:valid-message="importSuccessMessage"

packages/viewer/src/modules/menu/components/help/ReportProblemButton.vue

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { computed, type ComputedRef, nextTick, ref, useTemplateRef, watch } from
66
import { useI18n } from 'vue-i18n'
77
88
import type { ActionDispatcher } from '@/store/types'
9+
import type { ValidationResult } from '@/utils/composables/useFieldValidation'
910
1011
import sendFeedback, { ATTACHMENT_MAX_SIZE, KML_MAX_SIZE } from '@/api/feedback.api'
1112
import { getKmlUrl } from '@/api/files.api'
@@ -183,16 +184,16 @@ function closeAndCleanForm() {
183184
}
184185
}
185186
186-
function onTextValidate(valid: boolean) {
187-
isMessageValid.value = valid
187+
function onTextValidate(validation: ValidationResult) {
188+
isMessageValid.value = validation.valid
188189
}
189190
190-
function onAttachmentValidate(valid: boolean) {
191-
isAttachmentValid.value = valid
191+
function onAttachmentValidate(validation: ValidationResult) {
192+
isAttachmentValid.value = validation.valid
192193
}
193194
194-
function onEmailValidate(valid: boolean) {
195-
isEmailValid.value = valid
195+
function onEmailValidate(validation: ValidationResult) {
196+
isEmailValid.value = validation.valid
196197
}
197198
198199
async function generateShortLink() {

packages/viewer/src/modules/menu/components/help/feedback/FeedbackButton.vue

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import log from '@swissgeo/log'
33
import { computed, nextTick, ref, useTemplateRef } from 'vue'
44
import { useI18n } from 'vue-i18n'
55
6+
import type { ValidationResult } from '@/utils/composables/useFieldValidation'
7+
68
import sendFeedbackApi from '@/api/feedback.api'
79
import HeaderLink from '@/modules/menu/components/header/HeaderLink.vue'
810
import SendActionButtons from '@/modules/menu/components/help/common/SendActionButtons.vue'
@@ -111,12 +113,12 @@ function closeAndCleanForm() {
111113
request.value.completed = false
112114
}
113115
114-
function onTextValidate(valid: boolean) {
115-
isMessageValid.value = valid
116+
function onTextValidate(validation: ValidationResult) {
117+
isMessageValid.value = validation.valid
116118
}
117119
118-
function onEmailValidate(valid: boolean) {
119-
isEmailValid.value = valid
120+
function onEmailValidate(validation: ValidationResult) {
121+
isEmailValid.value = validation.valid
120122
}
121123
</script>
122124

packages/viewer/src/utils/components/EmailInput.vue

Lines changed: 110 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,111 +1,130 @@
11
<script setup lang="ts">
2-
import { toRefs, useTemplateRef } from 'vue'
2+
import { useTemplateRef } from 'vue'
33
import { useI18n } from 'vue-i18n'
44
55
import { useComponentUniqueId } from '@/utils/composables/useComponentUniqueId'
6-
import { useFieldValidation } from '@/utils/composables/useFieldValidation'
6+
import {
7+
useFieldValidation,
8+
type ValidateFunction,
9+
type ValidationResult,
10+
} from '@/utils/composables/useFieldValidation'
711
import { isValidEmail } from '@/utils/utils'
812
9-
const props = withDefaults(
10-
defineProps<{
11-
/** Label to add above the field */
12-
label?: string
13-
/** Description to add below the input */
14-
description?: string
15-
/** Mark the field as disable */
16-
disabled?: boolean
17-
/**
18-
* Placeholder text
19-
*
20-
* NOTE: this should be a translation key
21-
*/
22-
placeholder?: string
23-
/** Field is required and will be marked as invalid if empty */
24-
required?: boolean
25-
/**
26-
* Mark the field as valid
27-
*
28-
* This can be used if the field requires some external validation. When not set or set to
29-
* undefined this props is ignored.
30-
*
31-
* NOTE: this props is ignored when activate-validation is false
32-
*/
33-
validMarker?: boolean | undefined
34-
/**
35-
* Valid message Message that will be added in green below the field once the validation has
36-
* been done and the field is valid.
37-
*/
38-
validMessage?: string
39-
/**
40-
* Mark the field as invalid
41-
*
42-
* This can be used if the field requires some external validation. When not set or set to
43-
* undefined this props is ignored.
44-
*
45-
* NOTE: this props is ignored when activate-validation is false
46-
*/
47-
invalidMarker?: boolean | undefined
48-
/**
49-
* Invalid message Message that will be added in red below the field once the validation has
50-
* been done and the field is invalid.
51-
*
52-
* NOTE: this message is overwritten if the internal validation failed (not allow file type
53-
* or file too big or required empty file)
54-
*/
55-
invalidMessage?: string
56-
/**
57-
* Mark the field has validated.
58-
*
59-
* As long as the flag is false, no validation is run and no validation marks are set. Also
60-
* the props is-invalid and is-valid are ignored.
61-
*/
62-
activateValidation?: boolean
63-
/**
64-
* Validate function to run when the input changes The function should return an object of
65-
* type `{valid: Boolean, invalidMessage: String}`. The `invalidMessage` string should be a
66-
* translation key.
67-
*
68-
* NOTE: this function is called each time the field is modified
69-
*/
70-
validate?: ((_value?: string) => { valid: boolean; invalidMessage: string }) | undefined
71-
dataCy?: string
72-
}>(),
73-
{
74-
validMarker: undefined,
75-
invalidMarker: undefined,
76-
}
77-
)
78-
79-
const { label, description, disabled, placeholder, dataCy } = toRefs(props)
13+
const {
14+
label = '',
15+
description = '',
16+
disabled = false,
17+
placeholder = '',
18+
required = undefined,
19+
forceValid = undefined,
20+
validMessage,
21+
forceInvalid = undefined,
22+
invalidMessage,
23+
activateValidation = undefined,
24+
validate,
25+
dataCy = '',
26+
} = defineProps<{
27+
/** Label to add above the field */
28+
label?: string
29+
/** Description to add below the input */
30+
description?: string
31+
/** Mark the field as disable */
32+
disabled?: boolean
33+
/**
34+
* Placeholder text
35+
*
36+
* NOTE: this should be a translation key
37+
*/
38+
placeholder?: string
39+
/** Field is required and will be marked as invalid if empty */
40+
required?: boolean
41+
/**
42+
* Mark the field as valid
43+
*
44+
* This can be used if the field requires some external validation. When not set or set to
45+
* undefined this props is ignored.
46+
*
47+
* NOTE: this props is ignored when activate-validation is false
48+
*/
49+
forceValid?: boolean
50+
/**
51+
* Valid message Message that will be added in green below the field once the validation has
52+
* been done and the field is valid.
53+
*/
54+
validMessage?: string
55+
/**
56+
* Mark the field as invalid
57+
*
58+
* This can be used if the field requires some external validation. When not set or set to
59+
* undefined this props is ignored.
60+
*
61+
* NOTE: this props is ignored when activate-validation is false
62+
*/
63+
forceInvalid?: boolean
64+
/**
65+
* Invalid message Message that will be added in red below the field once the validation has
66+
* been done and the field is invalid.
67+
*
68+
* NOTE: this message is overwritten if the internal validation failed (not allow file type or
69+
* file too big or required empty file)
70+
*/
71+
invalidMessage?: string
72+
/**
73+
* Mark the field has validated.
74+
*
75+
* As long as the flag is false, no validation is run and no validation marks are set. Also the
76+
* props is-invalid and is-valid are ignored.
77+
*/
78+
activateValidation?: boolean
79+
/**
80+
* Validate function to run when the input changes The function should return an object of type
81+
* `{valid: Boolean, invalidMessage: String}`. The `invalidMessage` string should be a
82+
* translation key.
83+
*
84+
* NOTE: this function is called each time the field is modified
85+
*/
86+
validate?: ValidateFunction<string>
87+
dataCy?: string
88+
}>()
8089
8190
const inputEmailId = useComponentUniqueId('email-input')
82-
8391
const model = defineModel<string>({ default: '' })
92+
8493
const emits = defineEmits<{
8594
change: [void]
86-
validate: [isValid: boolean]
95+
validate: [validation: ValidationResult]
8796
focusin: [void]
8897
focusout: [void]
8998
'keydown.enter': [void]
9099
}>()
91100
const { t } = useI18n()
92101
93-
const {
94-
value,
95-
validMarker: computedValidMarker,
96-
invalidMarker: computedInvalidMarker,
97-
invalidMessage: computedInvalidMessage,
98-
onFocus,
99-
} = useFieldValidation(props, model, emits, {
100-
customValidate: validateEmail,
102+
const { validation, onFocus } = useFieldValidation<string>({
103+
model,
104+
105+
required,
101106
requiredInvalidMessage: 'no_email',
107+
108+
activateValidation,
109+
110+
forceValid,
111+
validFieldMessage: validMessage,
112+
113+
forceInvalid,
114+
invalidFieldMessage: invalidMessage ?? 'invalid_email',
115+
116+
emits,
117+
validate: validateEmail,
102118
})
103119
104120
const emailInputElement = useTemplateRef<HTMLInputElement>('emailInputElement')
105121
106-
function validateEmail() {
107-
if (value.value && !isValidEmail(value.value)) {
108-
return { valid: false, invalidMessage: 'invalid_email' }
122+
function validateEmail(email?: string): ValidationResult {
123+
if (email && !isValidEmail(email)) {
124+
return { valid: false, invalidMessage: invalidMessage ?? 'invalid_email' }
125+
}
126+
if (validate) {
127+
return validate(email)
109128
}
110129
return { valid: true, invalidMessage: '' }
111130
}
@@ -134,11 +153,11 @@ defineExpose({ focus })
134153
<input
135154
:id="inputEmailId"
136155
ref="emailInputElement"
137-
v-model="value"
156+
v-model="model"
138157
:disabled="disabled"
139158
:class="{
140-
'is-invalid': computedInvalidMarker,
141-
'is-valid': computedValidMarker,
159+
'is-invalid': validation && !validation.valid,
160+
'is-valid': validation && validation.valid,
142161
}"
143162
type="email"
144163
class="form-control"
@@ -150,11 +169,11 @@ defineExpose({ focus })
150169
@keydown.enter="emits('keydown.enter')"
151170
/>
152171
<div
153-
v-if="computedInvalidMessage"
172+
v-if="validation?.invalidMessage"
154173
class="invalid-feedback"
155174
data-cy="email-input-invalid-feedback"
156175
>
157-
{{ t(computedInvalidMessage) }}
176+
{{ t(validation.invalidMessage) }}
158177
</div>
159178
<div
160179
v-if="validMessage"

0 commit comments

Comments
 (0)