@dhis2/ui-forms 10.16.2 → 10.16.3-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +12 -11
- package/src/CheckboxFieldFF/CheckboxFieldFF.e2e.stories.js +52 -0
- package/src/CheckboxFieldFF/CheckboxFieldFF.js +52 -0
- package/src/CheckboxFieldFF/CheckboxFieldFF.prod.stories.js +142 -0
- package/src/CheckboxFieldFF/features/can_toggle_a_boolean/index.js +19 -0
- package/src/CheckboxFieldFF/features/can_toggle_a_boolean.feature +11 -0
- package/src/CheckboxFieldFF/features/can_toggle_a_value/index.js +21 -0
- package/src/CheckboxFieldFF/features/can_toggle_a_value.feature +11 -0
- package/src/CheckboxFieldFF/features/common/index.js +5 -0
- package/src/CheckboxFieldFF/features/displays_error/index.js +18 -0
- package/src/CheckboxFieldFF/features/displays_error.feature +6 -0
- package/src/FieldGroupFF/FieldGroupFF.js +42 -0
- package/src/FieldGroupFF/FieldGroupFF.prod.stories.js +49 -0
- package/src/FileInputFieldFF/FileInputFieldFF.e2e.stories.js +272 -0
- package/src/FileInputFieldFF/FileInputFieldFF.js +100 -0
- package/src/FileInputFieldFF/FileInputFieldFF.prod.stories.js +95 -0
- package/src/FileInputFieldFF/features/accepts_file/index.js +40 -0
- package/src/FileInputFieldFF/features/accepts_file.feature +13 -0
- package/src/FileInputFieldFF/features/common/index.js +9 -0
- package/src/FileInputFieldFF/features/displays_error/index.js +18 -0
- package/src/FileInputFieldFF/features/displays_error.feature +8 -0
- package/src/InputFieldFF/InputFieldFF.e2e.stories.js +19 -0
- package/src/InputFieldFF/InputFieldFF.js +58 -0
- package/src/InputFieldFF/InputFieldFF.prod.stories.js +102 -0
- package/src/InputFieldFF/features/can_set_a_value/index.js +14 -0
- package/src/InputFieldFF/features/can_set_a_value.feature +6 -0
- package/src/InputFieldFF/features/displays_error/index.js +15 -0
- package/src/InputFieldFF/features/displays_error.feature +6 -0
- package/src/MultiSelectFieldFF/MultiSelectFieldFF.e2e.stories.js +27 -0
- package/src/MultiSelectFieldFF/MultiSelectFieldFF.js +72 -0
- package/src/MultiSelectFieldFF/MultiSelectFieldFF.prod.stories.js +79 -0
- package/src/MultiSelectFieldFF/features/can_set_a_value/index.js +38 -0
- package/src/MultiSelectFieldFF/features/can_set_a_value.feature +14 -0
- package/src/MultiSelectFieldFF/features/common/index.js +7 -0
- package/src/MultiSelectFieldFF/features/displays_error/index.js +10 -0
- package/src/MultiSelectFieldFF/features/displays_error.feature +6 -0
- package/src/RadioFieldFF/RadioFieldFF.e2e.stories.js +39 -0
- package/src/RadioFieldFF/RadioFieldFF.js +52 -0
- package/src/RadioFieldFF/RadioFieldFF.prod.stories.js +104 -0
- package/src/RadioFieldFF/features/can_set_a_value/index.js +24 -0
- package/src/RadioFieldFF/features/can_set_a_value.feature +7 -0
- package/src/RadioFieldFF/features/common/index.js +9 -0
- package/src/RadioFieldFF/features/displays_error/index.js +13 -0
- package/src/RadioFieldFF/features/displays_error.feature +6 -0
- package/src/SimpleSingleSelectFieldFF/SimpleSingleSelectFieldFF.js +172 -0
- package/src/SimpleSingleSelectFieldFF/SimpleSingleSelectFieldFF.prod.stories.js +79 -0
- package/src/SimpleSingleSelectFieldFF/SimpleSingleSelectFieldFF.test.js +82 -0
- package/src/SingleSelectFieldFF/SingleSelectFieldFF.e2e.stories.js +23 -0
- package/src/SingleSelectFieldFF/SingleSelectFieldFF.js +70 -0
- package/src/SingleSelectFieldFF/SingleSelectFieldFF.prod.stories.js +77 -0
- package/src/SingleSelectFieldFF/features/can_set_a_value/index.js +22 -0
- package/src/SingleSelectFieldFF/features/can_set_a_value.feature +7 -0
- package/src/SingleSelectFieldFF/features/common/index.js +6 -0
- package/src/SingleSelectFieldFF/features/displays_error/index.js +10 -0
- package/src/SingleSelectFieldFF/features/displays_error.feature +6 -0
- package/src/SwitchFieldFF/SwitchFieldFF.e2e.stories.js +52 -0
- package/src/SwitchFieldFF/SwitchFieldFF.js +52 -0
- package/src/SwitchFieldFF/SwitchFieldFF.prod.stories.js +148 -0
- package/src/SwitchFieldFF/features/can_toggle_a_boolean/index.js +19 -0
- package/src/SwitchFieldFF/features/can_toggle_a_boolean.feature +11 -0
- package/src/SwitchFieldFF/features/can_toggle_a_value/index.js +21 -0
- package/src/SwitchFieldFF/features/can_toggle_a_value.feature +11 -0
- package/src/SwitchFieldFF/features/common/index.js +5 -0
- package/src/SwitchFieldFF/features/displays_error/index.js +18 -0
- package/src/SwitchFieldFF/features/displays_error.feature +6 -0
- package/src/TextAreaFieldFF/TextAreaFieldFF.e2e.stories.js +23 -0
- package/src/TextAreaFieldFF/TextAreaFieldFF.js +57 -0
- package/src/TextAreaFieldFF/TextAreaFieldFF.prod.stories.js +111 -0
- package/src/TextAreaFieldFF/features/can_set_a_value/index.js +14 -0
- package/src/TextAreaFieldFF/features/can_set_a_value.feature +6 -0
- package/src/TextAreaFieldFF/features/displays_error/index.js +15 -0
- package/src/TextAreaFieldFF/features/displays_error.feature +6 -0
- package/src/__tests__/__snapshots__/index.test.js.snap +65 -0
- package/src/__tests__/index.test.js +37 -0
- package/src/formDecorator.js +80 -0
- package/src/index.js +28 -0
- package/src/locales/ar/translations.json +30 -0
- package/src/locales/ar_IQ/translations.json +30 -0
- package/src/locales/ckb/translations.json +30 -0
- package/src/locales/cs/translations.json +30 -0
- package/src/locales/da/translations.json +30 -0
- package/src/locales/en/translations.json +30 -0
- package/src/locales/es/translations.json +30 -0
- package/src/locales/es_419/translations.json +30 -0
- package/src/locales/fr/translations.json +30 -0
- package/src/locales/hi_IN/translations.json +30 -0
- package/src/locales/id/translations.json +30 -0
- package/src/locales/index.js +84 -0
- package/src/locales/km/translations.json +30 -0
- package/src/locales/ko_KR/translations.json +30 -0
- package/src/locales/lo/translations.json +30 -0
- package/src/locales/my/translations.json +30 -0
- package/src/locales/nb/translations.json +30 -0
- package/src/locales/nl/translations.json +30 -0
- package/src/locales/prs/translations.json +30 -0
- package/src/locales/ps/translations.json +30 -0
- package/src/locales/pt/translations.json +30 -0
- package/src/locales/pt_BR/translations.json +30 -0
- package/src/locales/ro/translations.json +30 -0
- package/src/locales/ru/translations.json +30 -0
- package/src/locales/si/translations.json +30 -0
- package/src/locales/sv/translations.json +30 -0
- package/src/locales/tet/translations.json +30 -0
- package/src/locales/tg/translations.json +30 -0
- package/src/locales/uk/translations.json +30 -0
- package/src/locales/ur/translations.json +30 -0
- package/src/locales/uz_Latn/translations.json +30 -0
- package/src/locales/uz_UZ_Cyrl/translations.json +30 -0
- package/src/locales/uz_UZ_Latn/translations.json +30 -0
- package/src/locales/vi/translations.json +30 -0
- package/src/locales/zh/translations.json +30 -0
- package/src/locales/zh_CN/translations.json +30 -0
- package/src/shared/helpers/createBlurHandler.js +9 -0
- package/src/shared/helpers/createChangeHandler.js +21 -0
- package/src/shared/helpers/createFocusHandler.js +9 -0
- package/src/shared/helpers/createSelectChangeHandler.js +6 -0
- package/src/shared/helpers/createToggleChangeHandler.js +9 -0
- package/src/shared/helpers/getValidationText.js +21 -0
- package/src/shared/helpers/hasError.js +3 -0
- package/src/shared/helpers/isLoading.js +4 -0
- package/src/shared/helpers/isValid.js +4 -0
- package/src/shared/helpers.js +9 -0
- package/src/shared/propTypes.js +48 -0
- package/src/transformers/arrayWithIdObjects.js +8 -0
- package/src/transformers/index.js +1 -0
- package/src/validators/__tests__/alphaNumeric.test.js +29 -0
- package/src/validators/__tests__/boolean.test.js +23 -0
- package/src/validators/__tests__/composeValidators.test.js +23 -0
- package/src/validators/__tests__/createCharacterLengthRange.test.js +59 -0
- package/src/validators/__tests__/createEqualTo.test.js +43 -0
- package/src/validators/__tests__/createMaxCharacterLength.test.js +24 -0
- package/src/validators/__tests__/createMaxNumber.test.js +21 -0
- package/src/validators/__tests__/createMinCharacterLength.test.js +24 -0
- package/src/validators/__tests__/createMinNumber.test.js +21 -0
- package/src/validators/__tests__/createNumberRange.test.js +68 -0
- package/src/validators/__tests__/createPattern.test.js +40 -0
- package/src/validators/__tests__/dhis2Password.test.js +51 -0
- package/src/validators/__tests__/dhis2Username.test.js +75 -0
- package/src/validators/__tests__/email.test.js +83 -0
- package/src/validators/__tests__/hasValue.test.js +21 -0
- package/src/validators/__tests__/integer.test.js +48 -0
- package/src/validators/__tests__/internationalPhoneNumber.test.js +49 -0
- package/src/validators/__tests__/number.test.js +39 -0
- package/src/validators/__tests__/string.test.js +29 -0
- package/src/validators/__tests__/url.test.js +106 -0
- package/src/validators/alphaNumeric.js +15 -0
- package/src/validators/boolean.js +11 -0
- package/src/validators/composeValidators.js +8 -0
- package/src/validators/createCharacterLengthRange.js +27 -0
- package/src/validators/createEqualTo.js +16 -0
- package/src/validators/createMaxCharacterLength.js +13 -0
- package/src/validators/createMaxNumber.js +13 -0
- package/src/validators/createMinCharacterLength.js +13 -0
- package/src/validators/createMinNumber.js +13 -0
- package/src/validators/createNumberRange.js +28 -0
- package/src/validators/createPattern.js +22 -0
- package/src/validators/dhis2Password.js +81 -0
- package/src/validators/dhis2Username.js +16 -0
- package/src/validators/email.js +38 -0
- package/src/validators/hasValue.js +8 -0
- package/src/validators/helpers/index.js +23 -0
- package/src/validators/index.js +20 -0
- package/src/validators/integer.js +20 -0
- package/src/validators/internationalPhoneNumber.js +56 -0
- package/src/validators/number.js +9 -0
- package/src/validators/string.js +9 -0
- package/src/validators/test-helpers/index.js +21 -0
- package/src/validators/url.js +15 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import i18n from '../locales/index.js'
|
|
2
|
+
import { isEmpty, isString } from './helpers/index.js'
|
|
3
|
+
|
|
4
|
+
const LOWER_CASE_PATTERN = /^(?=.*[a-z]).+$/
|
|
5
|
+
const UPPER_CASE_PATTERN = /^(?=.*[A-Z]).+$/
|
|
6
|
+
const DIGIT_PATTERN = /^(?=.*[0-9]).+$/
|
|
7
|
+
// Using this regex to match all non-alphanumeric characters to match server-side implementation
|
|
8
|
+
// https://github.com/dhis2/dhis2-core/blob/master/dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/user/SpecialCharacterValidationRule.java#L39
|
|
9
|
+
const SPECIAL_CHARACTER_PATTERN = /[^a-zA-Z0-9]/
|
|
10
|
+
|
|
11
|
+
const notString = i18n.t('Password should be a string')
|
|
12
|
+
const tooShort = i18n.t('Password should be at least 8 characters long')
|
|
13
|
+
const tooLong = i18n.t('Password should be no longer than 34 characters')
|
|
14
|
+
const noLowerCase = i18n.t(
|
|
15
|
+
'Password should contain at least one lowercase letter'
|
|
16
|
+
)
|
|
17
|
+
const noUpperCase = i18n.t(
|
|
18
|
+
'Password should contain at least one UPPERCASE letter'
|
|
19
|
+
)
|
|
20
|
+
const noNumber = i18n.t('Password should contain at least one number')
|
|
21
|
+
const noSpecialCharacter = i18n.t(
|
|
22
|
+
'Password should have at least one special character'
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Tests if a given password is compliant with the password restrictions.
|
|
27
|
+
* This function checks all restrictions below, but returns when the first violation was found:
|
|
28
|
+
* - At least 8 characters
|
|
29
|
+
* - No more than 34 characters
|
|
30
|
+
* - Contains at least 1 lowercase character
|
|
31
|
+
* - Contains at least 1 UPPERCASE character
|
|
32
|
+
* - Contains at least 1 number
|
|
33
|
+
* - Contains at least 1 special character
|
|
34
|
+
*/
|
|
35
|
+
const dhis2Password = (value) => {
|
|
36
|
+
if (isEmpty(value)) {
|
|
37
|
+
return undefined
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (!isString(value)) {
|
|
41
|
+
return notString
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (value.length < 8) {
|
|
45
|
+
return tooShort
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (value.length > 35) {
|
|
49
|
+
return tooLong
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (!LOWER_CASE_PATTERN.test(value)) {
|
|
53
|
+
return noLowerCase
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (!UPPER_CASE_PATTERN.test(value)) {
|
|
57
|
+
return noUpperCase
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!DIGIT_PATTERN.test(value)) {
|
|
61
|
+
return noNumber
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!SPECIAL_CHARACTER_PATTERN.test(value)) {
|
|
65
|
+
return noSpecialCharacter
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return undefined
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const errorMessages = {
|
|
72
|
+
notString,
|
|
73
|
+
tooShort,
|
|
74
|
+
tooLong,
|
|
75
|
+
noLowerCase,
|
|
76
|
+
noUpperCase,
|
|
77
|
+
noNumber,
|
|
78
|
+
noSpecialCharacter,
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export { dhis2Password, errorMessages }
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import i18n from '../locales/index.js'
|
|
2
|
+
import { isEmpty, isString } from './helpers/index.js'
|
|
3
|
+
|
|
4
|
+
const USERNAME_PATTERN =
|
|
5
|
+
/^(?=.{4,255}$)(?![_\-.@])(?!.*[_\-.@]{2})[a-zA-Z0-9._\-@]+(?<![_\-.@])$/
|
|
6
|
+
|
|
7
|
+
const invalidUsernameMessage = i18n.t(
|
|
8
|
+
'Please provide a username between 4 and 255 characters long and possibly separated by . _ - or @'
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
const dhis2Username = (value) =>
|
|
12
|
+
isEmpty(value) || (isString(value) && USERNAME_PATTERN.test(value))
|
|
13
|
+
? undefined
|
|
14
|
+
: invalidUsernameMessage
|
|
15
|
+
|
|
16
|
+
export { dhis2Username, invalidUsernameMessage }
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import i18n from '../locales/index.js'
|
|
2
|
+
import { isEmpty, isString } from './helpers/index.js'
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* Email validation is complicated business. There is no perfect regex,
|
|
6
|
+
* instead we have to make a trade-off between complexity, correctness,
|
|
7
|
+
* and the risk of producing false negatives. This article
|
|
8
|
+
* https://www.regular-expressions.info/email.html offers a good overview.
|
|
9
|
+
* It recommends to use a very simple regex when having to validate many
|
|
10
|
+
* records, but for validating an individual email address a more complex
|
|
11
|
+
* regex may be used.
|
|
12
|
+
*
|
|
13
|
+
* The pattern below is taken from the "The Official Standard: RFC 5322"
|
|
14
|
+
* section of the article and is described as:
|
|
15
|
+
* "[..] a more practical implementation of RFC 5322 [..] that will still
|
|
16
|
+
* match 99.99% of all email addresses in actual use today"
|
|
17
|
+
*
|
|
18
|
+
* const EMAIL_ADDRESS_PATTERN = /[a-z0-9!#$%&'*+/=?^_‘{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_‘{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/i
|
|
19
|
+
*
|
|
20
|
+
* However, this regex produces a few false negatives and quite a lot
|
|
21
|
+
* of false positives.
|
|
22
|
+
*
|
|
23
|
+
* Another regex, found in this stackoverflow answer below resulted in a better
|
|
24
|
+
* overall picture in terms of false negatives and positives, so I settled on that one:
|
|
25
|
+
* https://stackoverflow.com/questions/46155/how-to-validate-an-email-address-in-javascript/46181#46181
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
const EMAIL_ADDRESS_PATTERN =
|
|
29
|
+
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/i
|
|
30
|
+
|
|
31
|
+
const invalidEmailMessage = i18n.t('Please provide a valid email address')
|
|
32
|
+
|
|
33
|
+
const email = (value) =>
|
|
34
|
+
isEmpty(value) || (isString(value) && EMAIL_ADDRESS_PATTERN.test(value))
|
|
35
|
+
? undefined
|
|
36
|
+
: invalidEmailMessage
|
|
37
|
+
|
|
38
|
+
export { email, invalidEmailMessage }
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import i18n from '../locales/index.js'
|
|
2
|
+
import { isEmpty } from './helpers/index.js'
|
|
3
|
+
|
|
4
|
+
const hasValueMessage = i18n.t('Please provide a value')
|
|
5
|
+
|
|
6
|
+
const hasValue = (value) => (isEmpty(value) ? hasValueMessage : undefined)
|
|
7
|
+
|
|
8
|
+
export { hasValue, hasValueMessage }
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export const isEmpty = (value) =>
|
|
2
|
+
typeof value === 'undefined' || value === null || value === ''
|
|
3
|
+
|
|
4
|
+
export const isString = (value) => typeof value === 'string'
|
|
5
|
+
|
|
6
|
+
export const isNumber = (value) => typeof value === 'number'
|
|
7
|
+
|
|
8
|
+
export const isNumeric = (value) =>
|
|
9
|
+
(isString(value) || isNumber(value)) && !isNaN(value)
|
|
10
|
+
|
|
11
|
+
export const isInRange = (lowerBound, upperBound, value) =>
|
|
12
|
+
value >= lowerBound && value <= upperBound
|
|
13
|
+
|
|
14
|
+
export const toNumber = (value) => Number(value)
|
|
15
|
+
|
|
16
|
+
export const requiredArgumentErrorMessage =
|
|
17
|
+
'Incorrect arguments provided when creating validator'
|
|
18
|
+
|
|
19
|
+
export const requireArgument = (value, type) => {
|
|
20
|
+
if (isEmpty(value) || typeof value !== type) {
|
|
21
|
+
throw new Error(requiredArgumentErrorMessage)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export { alphaNumeric } from './alphaNumeric.js'
|
|
2
|
+
export { boolean } from './boolean.js'
|
|
3
|
+
export { composeValidators } from './composeValidators.js'
|
|
4
|
+
export { createCharacterLengthRange } from './createCharacterLengthRange.js'
|
|
5
|
+
export { createEqualTo } from './createEqualTo.js'
|
|
6
|
+
export { createMaxCharacterLength } from './createMaxCharacterLength.js'
|
|
7
|
+
export { createMaxNumber } from './createMaxNumber.js'
|
|
8
|
+
export { createMinCharacterLength } from './createMinCharacterLength.js'
|
|
9
|
+
export { createMinNumber } from './createMinNumber.js'
|
|
10
|
+
export { createNumberRange } from './createNumberRange.js'
|
|
11
|
+
export { createPattern } from './createPattern.js'
|
|
12
|
+
export { dhis2Password } from './dhis2Password.js'
|
|
13
|
+
export { dhis2Username } from './dhis2Username.js'
|
|
14
|
+
export { email } from './email.js'
|
|
15
|
+
export { hasValue } from './hasValue.js'
|
|
16
|
+
export { integer } from './integer.js'
|
|
17
|
+
export { internationalPhoneNumber } from './internationalPhoneNumber.js'
|
|
18
|
+
export { number } from './number.js'
|
|
19
|
+
export { string } from './string.js'
|
|
20
|
+
export { url } from './url.js'
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import i18n from '../locales/index.js'
|
|
2
|
+
import { isEmpty, isNumeric, toNumber } from './helpers/index.js'
|
|
3
|
+
|
|
4
|
+
const invalidIntegerMessage = i18n.t(
|
|
5
|
+
'Please provide a round number without decimals'
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
// Regex accepts only digits (no decimals even if it is trailing like 4.0)
|
|
9
|
+
// it also rejects a leading 0 (i.e 04) as this is rejected by backend
|
|
10
|
+
const INTEGER_PATTERN = /^(-?[1-9]\d*|0)$/
|
|
11
|
+
|
|
12
|
+
const integer = (value) =>
|
|
13
|
+
isEmpty(value) ||
|
|
14
|
+
(INTEGER_PATTERN.test(value) &&
|
|
15
|
+
isNumeric(value) &&
|
|
16
|
+
Number.isSafeInteger(toNumber(value)))
|
|
17
|
+
? undefined
|
|
18
|
+
: invalidIntegerMessage
|
|
19
|
+
|
|
20
|
+
export { integer, invalidIntegerMessage }
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import i18n from '../locales/index.js'
|
|
2
|
+
import { isEmpty, isString, isNumeric } from './helpers/index.js'
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* There were some problems with the server side implementation
|
|
6
|
+
* of how international phone numbers are validated, and the
|
|
7
|
+
* server side implementation will likely be removed, see:
|
|
8
|
+
* https://jira.dhis2.org/browse/DHIS2-8040
|
|
9
|
+
*
|
|
10
|
+
* So, rather than aligning with the server-side implementation
|
|
11
|
+
* this validator implements the E.164 numbering plan, see:
|
|
12
|
+
* https://www.cm.com/blog/how-to-format-international-telephone-numbers/
|
|
13
|
+
*
|
|
14
|
+
* SPECS
|
|
15
|
+
* Here's how the E.164 numbering plan works:
|
|
16
|
+
* - A telephone number can have a maximum of 15 digits
|
|
17
|
+
* - The first part of the telephone number is the country code (one to three digits)
|
|
18
|
+
* - The second part is the national destination code (NDC)
|
|
19
|
+
* - The last part is the subscriber number (SN)
|
|
20
|
+
* - The NDC and SN together are collectively called the national (significant) number
|
|
21
|
+
*
|
|
22
|
+
* IMPLEMENTATION ADVICE
|
|
23
|
+
* Two important things to note: First of all, in the international E.164 notation a
|
|
24
|
+
* leading ‘0’ is removed. The UK mobile phone number ‘07911 123456’ in international
|
|
25
|
+
* format is ‘+44 7911 123456’, so without the first zero. Secondly in the E.164 notation
|
|
26
|
+
* all spaces, dashes [‘-‘] and parentheses [ ‘(‘ and ‘)’] are removed, besides the
|
|
27
|
+
* leading ‘+’ all characters should be numeric.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
const invalidInternationalPhoneNumberMessage = i18n.t(
|
|
31
|
+
'Please provide a valid international phone number.'
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
const internationalPhoneNumber = (value) => {
|
|
35
|
+
// allow empty values
|
|
36
|
+
if (isEmpty(value)) {
|
|
37
|
+
return undefined
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// value must be a string
|
|
41
|
+
if (!isString(value)) {
|
|
42
|
+
return invalidInternationalPhoneNumberMessage
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const cleanedValue = value
|
|
46
|
+
// strip all hyphens, dots, spaces
|
|
47
|
+
.replace(/[-. )(]/g, '')
|
|
48
|
+
// trim leading zeroes and plus signs
|
|
49
|
+
.replace(/^[0+]+/, '')
|
|
50
|
+
|
|
51
|
+
return isNumeric(cleanedValue) && cleanedValue.length <= 15
|
|
52
|
+
? undefined
|
|
53
|
+
: invalidInternationalPhoneNumberMessage
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export { internationalPhoneNumber, invalidInternationalPhoneNumberMessage }
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import i18n from '../locales/index.js'
|
|
2
|
+
import { isEmpty, isNumeric } from './helpers/index.js'
|
|
3
|
+
|
|
4
|
+
const invalidNumberMessage = i18n.t('Please provide a number')
|
|
5
|
+
|
|
6
|
+
const number = (value) =>
|
|
7
|
+
isEmpty(value) || isNumeric(value) ? undefined : invalidNumberMessage
|
|
8
|
+
|
|
9
|
+
export { number, invalidNumberMessage }
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import i18n from '../locales/index.js'
|
|
2
|
+
import { isEmpty, isString } from './helpers/index.js'
|
|
3
|
+
|
|
4
|
+
const invalidStringMessage = i18n.t('Please provide a string')
|
|
5
|
+
|
|
6
|
+
const string = (value) =>
|
|
7
|
+
isEmpty(value) || isString(value) ? undefined : invalidStringMessage
|
|
8
|
+
|
|
9
|
+
export { string, invalidStringMessage }
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const testValidatorValues = (validator, returnValue, values) => {
|
|
2
|
+
const returnValueStr =
|
|
3
|
+
returnValue === undefined ? 'undefined' : 'an error string'
|
|
4
|
+
|
|
5
|
+
for (const value of values) {
|
|
6
|
+
const type = typeof value
|
|
7
|
+
const valueStr = type === 'object' ? JSON.stringify(value) : value
|
|
8
|
+
|
|
9
|
+
it(`should return ${returnValueStr} for value \`${valueStr}\` of type ${type}`, () => {
|
|
10
|
+
expect(validator(value)).toBe(returnValue)
|
|
11
|
+
})
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const allowsEmptyValues = (validator) => {
|
|
16
|
+
describe('allows empty values', () => {
|
|
17
|
+
testValidatorValues(validator, undefined, ['', null, undefined])
|
|
18
|
+
})
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { testValidatorValues, allowsEmptyValues }
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import i18n from '../locales/index.js'
|
|
2
|
+
import { isEmpty, isString } from './helpers/index.js'
|
|
3
|
+
|
|
4
|
+
// Source: https://gist.github.com/dperini/729294
|
|
5
|
+
const URL_PATTERN =
|
|
6
|
+
/^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:[/?#]\S*)?$/i
|
|
7
|
+
|
|
8
|
+
const invalidUrlMessage = i18n.t('Please provide a valid url')
|
|
9
|
+
|
|
10
|
+
const url = (value) =>
|
|
11
|
+
isEmpty(value) || (isString(value) && URL_PATTERN.test(value))
|
|
12
|
+
? undefined
|
|
13
|
+
: invalidUrlMessage
|
|
14
|
+
|
|
15
|
+
export { url, invalidUrlMessage }
|