@openmrs/esm-form-engine-lib 2.1.0-pre.1362
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/.editorconfig +12 -0
- package/.eslintignore +2 -0
- package/.eslintrc +58 -0
- package/.husky/pre-commit +6 -0
- package/.husky/pre-push +6 -0
- package/.prettierignore +4 -0
- package/LICENSE.txt +401 -0
- package/README.md +136 -0
- package/__mocks__/concepts.mock.json +140 -0
- package/__mocks__/forms/afe-forms/component_art.json +38 -0
- package/__mocks__/forms/afe-forms/component_preclinic-review.json +38 -0
- package/__mocks__/forms/afe-forms/demo_hts-form.json +62 -0
- package/__mocks__/forms/afe-forms/form-component.json +38 -0
- package/__mocks__/forms/afe-forms/mini-form.json +31 -0
- package/__mocks__/forms/afe-forms/nested-form1.json +38 -0
- package/__mocks__/forms/afe-forms/nested-form2.json +38 -0
- package/__mocks__/forms/afe-forms/test-orders.json +72 -0
- package/__mocks__/forms/afe-forms/test-schema-transformer-form.json +88 -0
- package/__mocks__/forms/rfe-forms/age-validation-form.json +58 -0
- package/__mocks__/forms/rfe-forms/bmi-test-form.json +69 -0
- package/__mocks__/forms/rfe-forms/bsa-test-form.json +69 -0
- package/__mocks__/forms/rfe-forms/component_art.json +1705 -0
- package/__mocks__/forms/rfe-forms/component_preclinic-review.json +480 -0
- package/__mocks__/forms/rfe-forms/conditional-answered-form.json +97 -0
- package/__mocks__/forms/rfe-forms/conditional-required-form.json +281 -0
- package/__mocks__/forms/rfe-forms/demo_hts-form.json +346 -0
- package/__mocks__/forms/rfe-forms/edd-test-form.json +88 -0
- package/__mocks__/forms/rfe-forms/external_data_source_form.json +43 -0
- package/__mocks__/forms/rfe-forms/filter-answer-options-test-form.json +87 -0
- package/__mocks__/forms/rfe-forms/form-component.json +43 -0
- package/__mocks__/forms/rfe-forms/forms-loader.test.schema.ts +209 -0
- package/__mocks__/forms/rfe-forms/historical-expressions-form.json +170 -0
- package/__mocks__/forms/rfe-forms/labour_and_delivery_test_form.json +374 -0
- package/__mocks__/forms/rfe-forms/mini-form.json +29 -0
- package/__mocks__/forms/rfe-forms/mockHistoricalvisitsEncounter.json +89 -0
- package/__mocks__/forms/rfe-forms/months-on-art-form.json +90 -0
- package/__mocks__/forms/rfe-forms/multi-select-form.json +86 -0
- package/__mocks__/forms/rfe-forms/nested-form1.json +43 -0
- package/__mocks__/forms/rfe-forms/nested-form2.json +43 -0
- package/__mocks__/forms/rfe-forms/next-visit-test-form.json +78 -0
- package/__mocks__/forms/rfe-forms/obs-group-test_form.json +137 -0
- package/__mocks__/forms/rfe-forms/obs-list-data.ts +37 -0
- package/__mocks__/forms/rfe-forms/post-submission-test-form.json +116 -0
- package/__mocks__/forms/rfe-forms/reference-by-mapping-form.json +54 -0
- package/__mocks__/forms/rfe-forms/required-form.json +50 -0
- package/__mocks__/forms/rfe-forms/sample_fields.json +36 -0
- package/__mocks__/forms/rfe-forms/test-enrolment-form.json +241 -0
- package/__mocks__/forms/rfe-forms/treatment-end-date-test-form.json +121 -0
- package/__mocks__/forms/rfe-forms/viral-load-status-form.json +75 -0
- package/__mocks__/forms/rfe-forms/zscore-bmi-for-age-form.json +79 -0
- package/__mocks__/forms/rfe-forms/zscore-height-for-age-form.json +79 -0
- package/__mocks__/forms/rfe-forms/zscore-weight-height-form.json +77 -0
- package/__mocks__/packages/hiv/forms/hts_poc/1.0.json +8 -0
- package/__mocks__/packages/hiv/forms/hts_poc/1.1.json +91 -0
- package/__mocks__/packages/test-forms-registry.ts +12 -0
- package/__mocks__/patient.mock.ts +173 -0
- package/__mocks__/react-i18next.js +49 -0
- package/__mocks__/react-markdown.tsx +5 -0
- package/__mocks__/session.mock.ts +117 -0
- package/__mocks__/single-spa-react.js +11 -0
- package/__mocks__/use-initial-values/encounter.mock.json +963 -0
- package/__mocks__/use-initial-values/patient.mock.json +73 -0
- package/__mocks__/visit.mock.ts +19 -0
- package/dist/openmrs-esm-form-engine-lib.js +1 -0
- package/jest.config.js +30 -0
- package/package.json +104 -0
- package/prettier.config.js +8 -0
- package/readme/form-engine.jpeg +0 -0
- package/src/adapters/control-adapter.ts +29 -0
- package/src/adapters/encounter-datetime-adapter.ts +38 -0
- package/src/adapters/encounter-location-adapter.ts +39 -0
- package/src/adapters/encounter-provider-adapter.ts +48 -0
- package/src/adapters/encounter-role-adapter.ts +54 -0
- package/src/adapters/inline-date-adapter.ts +58 -0
- package/src/adapters/obs-adapter.ts +280 -0
- package/src/adapters/obs-comment-adapter.ts +60 -0
- package/src/adapters/orders-adapter.ts +75 -0
- package/src/adapters/patient-identifier-adapter.ts +40 -0
- package/src/adapters/program-state-adapter.ts +52 -0
- package/src/api/index.ts +178 -0
- package/src/components/error/error-modal.component.tsx +37 -0
- package/src/components/error/error.scss +4 -0
- package/src/components/extension/extension-parcel.component.tsx +32 -0
- package/src/components/field-label/field-label.component.tsx +32 -0
- package/src/components/field-label/field-label.scss +11 -0
- package/src/components/group/obs-group.component.tsx +29 -0
- package/src/components/group/obs-group.scss +12 -0
- package/src/components/inputs/content-switcher/content-switcher.component.tsx +71 -0
- package/src/components/inputs/content-switcher/content-switcher.scss +55 -0
- package/src/components/inputs/date/date.component.tsx +149 -0
- package/src/components/inputs/date/date.scss +36 -0
- package/src/components/inputs/file/camera/camera.component.tsx +34 -0
- package/src/components/inputs/file/camera/camera.scss +3 -0
- package/src/components/inputs/file/file.component.tsx +159 -0
- package/src/components/inputs/file/file.scss +101 -0
- package/src/components/inputs/fixed-value/fixed-value.component.tsx +19 -0
- package/src/components/inputs/markdown/markdown-wrapper.component.tsx +14 -0
- package/src/components/inputs/markdown/markdown.component.tsx +8 -0
- package/src/components/inputs/multi-select/multi-select.component.tsx +151 -0
- package/src/components/inputs/multi-select/multi-select.scss +25 -0
- package/src/components/inputs/multi-select/multi-select.test.tsx +90 -0
- package/src/components/inputs/number/number.component.tsx +69 -0
- package/src/components/inputs/number/number.scss +15 -0
- package/src/components/inputs/radio/radio.component.tsx +79 -0
- package/src/components/inputs/radio/radio.scss +36 -0
- package/src/components/inputs/select/dropdown.component.tsx +73 -0
- package/src/components/inputs/select/dropdown.scss +11 -0
- package/src/components/inputs/select/dropdown.test.tsx +120 -0
- package/src/components/inputs/text/text.component.tsx +65 -0
- package/src/components/inputs/text/text.scss +15 -0
- package/src/components/inputs/text/text.test.tsx +104 -0
- package/src/components/inputs/text-area/text-area.component.tsx +63 -0
- package/src/components/inputs/text-area/text-area.scss +11 -0
- package/src/components/inputs/toggle/toggle.component.tsx +66 -0
- package/src/components/inputs/toggle/toggle.scss +12 -0
- package/src/components/inputs/tooltip/tooltip.component.tsx +23 -0
- package/src/components/inputs/tooltip/tooltip.scss +8 -0
- package/src/components/inputs/ui-select-extended/ui-select-extended.component.tsx +187 -0
- package/src/components/inputs/ui-select-extended/ui-select-extended.scss +15 -0
- package/src/components/inputs/ui-select-extended/ui-select-extended.test.tsx +211 -0
- package/src/components/inputs/unspecified/unspecified.component.tsx +74 -0
- package/src/components/inputs/unspecified/unspecified.scss +7 -0
- package/src/components/inputs/unspecified/unspecified.test.tsx +95 -0
- package/src/components/inputs/workspace-launcher/workspace-launcher.component.tsx +35 -0
- package/src/components/inputs/workspace-launcher/workspace-launcher.scss +15 -0
- package/src/components/label/label.component.tsx +20 -0
- package/src/components/label/label.scss +11 -0
- package/src/components/loaders/loader.component.tsx +16 -0
- package/src/components/loaders/loader.scss +20 -0
- package/src/components/patient-banner/patient-banner.component.tsx +20 -0
- package/src/components/patient-banner/patient-banner.scss +12 -0
- package/src/components/previous-value-review/previous-value-review.component.tsx +49 -0
- package/src/components/previous-value-review/previous-value-review.scss +36 -0
- package/src/components/processor-factory/form-processor-factory.component.tsx +127 -0
- package/src/components/renderer/custom-hooks-renderer.component.tsx +30 -0
- package/src/components/renderer/field/fieldLogic.ts +214 -0
- package/src/components/renderer/field/form-field-renderer.component.tsx +281 -0
- package/src/components/renderer/field/form-field-renderer.scss +5 -0
- package/src/components/renderer/form/form-renderer.component.tsx +89 -0
- package/src/components/renderer/form/state.ts +54 -0
- package/src/components/renderer/page/page.renderer.component.tsx +50 -0
- package/src/components/renderer/page/page.renderer.scss +36 -0
- package/src/components/renderer/section/section-renderer.component.tsx +21 -0
- package/src/components/renderer/section/section-renderer.scss +19 -0
- package/src/components/repeat/helpers.test.ts +29 -0
- package/src/components/repeat/helpers.ts +68 -0
- package/src/components/repeat/repeat-controls.component.tsx +38 -0
- package/src/components/repeat/repeat-controls.scss +7 -0
- package/src/components/repeat/repeat.component.tsx +201 -0
- package/src/components/repeat/repeat.scss +30 -0
- package/src/components/repeat/repeat.test.ts +29 -0
- package/src/components/sidebar/sidebar.component.tsx +134 -0
- package/src/components/sidebar/sidebar.scss +121 -0
- package/src/components/value/value.component.tsx +27 -0
- package/src/components/value/value.scss +17 -0
- package/src/components/value/view/field-value-view.component.tsx +33 -0
- package/src/components/value/view/field-value-view.scss +31 -0
- package/src/constants.ts +12 -0
- package/src/datasources/concept-data-source.ts +42 -0
- package/src/datasources/data-source.ts +23 -0
- package/src/datasources/encounter-role-datasource.ts +15 -0
- package/src/datasources/historical-data-source.ts +11 -0
- package/src/datasources/location-data-source.ts +27 -0
- package/src/datasources/provider-datasource.ts +15 -0
- package/src/datasources/select-concept-answers-datasource.ts +15 -0
- package/src/declarations.d.ts +4 -0
- package/src/external-function-context.tsx +8 -0
- package/src/form-context.tsx +42 -0
- package/src/form-engine.component.tsx +178 -0
- package/src/form-engine.scss +140 -0
- package/src/form-engine.test.tsx +817 -0
- package/src/globals.ts +1 -0
- package/src/hooks/useClobData.tsx +21 -0
- package/src/hooks/useConcepts.tsx +55 -0
- package/src/hooks/useDatasourceDependentValue.ts +16 -0
- package/src/hooks/useEncounter.tsx +32 -0
- package/src/hooks/useEncounterRole.tsx +15 -0
- package/src/hooks/useEvaluateFormFieldExpressions.ts +138 -0
- package/src/hooks/useFieldValidationResults.ts +18 -0
- package/src/hooks/useFormCollapse.tsx +36 -0
- package/src/hooks/useFormFieldValidators.ts +22 -0
- package/src/hooks/useFormFieldValueAdapters.ts +24 -0
- package/src/hooks/useFormFields.ts +37 -0
- package/src/hooks/useFormFieldsMeta.ts +48 -0
- package/src/hooks/useFormJson.test.tsx +173 -0
- package/src/hooks/useFormJson.tsx +237 -0
- package/src/hooks/useFormStateHelpers.ts +50 -0
- package/src/hooks/useFormsConfig.tsx +27 -0
- package/src/hooks/useInitialValues.ts +38 -0
- package/src/hooks/usePatientData.tsx +32 -0
- package/src/hooks/usePatientPrograms.ts +32 -0
- package/src/hooks/usePostSubmissionActions.test.tsx +42 -0
- package/src/hooks/usePostSubmissionActions.ts +31 -0
- package/src/hooks/useProcessorDependencies.ts +30 -0
- package/src/hooks/useRestMaxResultsCount.ts +5 -0
- package/src/hooks/useSystemSetting.ts +36 -0
- package/src/hooks/useWorkspaceLayout.ts +29 -0
- package/src/index.ts +12 -0
- package/src/lifecycle.ts +33 -0
- package/src/post-submission-actions/program-enrollment-action.ts +138 -0
- package/src/processors/encounter/encounter-form-processor.ts +337 -0
- package/src/processors/encounter/encounter-processor-helper.ts +320 -0
- package/src/processors/form-processor.ts +41 -0
- package/src/provider/form-factory-helper.ts +100 -0
- package/src/provider/form-factory-provider.tsx +169 -0
- package/src/provider/form-provider.tsx +37 -0
- package/src/registry/inbuilt-components/InbuiltPostSubmissionActions.ts +9 -0
- package/src/registry/inbuilt-components/control-templates.ts +57 -0
- package/src/registry/inbuilt-components/inbuiltControls.ts +99 -0
- package/src/registry/inbuilt-components/inbuiltDataSources.ts +41 -0
- package/src/registry/inbuilt-components/inbuiltFieldValueAdapters.ts +64 -0
- package/src/registry/inbuilt-components/inbuiltTransformers.ts +10 -0
- package/src/registry/inbuilt-components/inbuiltValidators.ts +33 -0
- package/src/registry/inbuilt-components/template-component-map.ts +28 -0
- package/src/registry/registry.test.ts +20 -0
- package/src/registry/registry.ts +261 -0
- package/src/routes.json +1 -0
- package/src/setupI18n.ts +16 -0
- package/src/setupTests.ts +5 -0
- package/src/transformers/default-schema-transformer.test.ts +155 -0
- package/src/transformers/default-schema-transformer.ts +239 -0
- package/src/types/domain.ts +129 -0
- package/src/types/index.ts +130 -0
- package/src/types/schema.ts +238 -0
- package/src/typings.d.ts +9 -0
- package/src/utils/boolean-utils.ts +25 -0
- package/src/utils/common-expression-helpers.ts +503 -0
- package/src/utils/common-utils.test.ts +136 -0
- package/src/utils/common-utils.ts +55 -0
- package/src/utils/error-utils.ts +37 -0
- package/src/utils/expression-parser.test.ts +308 -0
- package/src/utils/expression-parser.ts +158 -0
- package/src/utils/expression-runner.test.ts +387 -0
- package/src/utils/expression-runner.ts +219 -0
- package/src/utils/form-helper.test.ts +482 -0
- package/src/utils/form-helper.ts +210 -0
- package/src/utils/form-page-utils.ts +13 -0
- package/src/utils/forms-loader.test.ts +323 -0
- package/src/utils/forms-loader.ts +306 -0
- package/src/utils/post-submission-action-helper.ts +71 -0
- package/src/utils/test-utils.ts +54 -0
- package/src/utils/zscore-service.ts +59 -0
- package/src/validators/conditional-answered-validator.test.ts +61 -0
- package/src/validators/conditional-answered-validator.ts +17 -0
- package/src/validators/date-validator.test.ts +46 -0
- package/src/validators/date-validator.ts +19 -0
- package/src/validators/default-value-validator.test.ts +90 -0
- package/src/validators/default-value-validator.ts +36 -0
- package/src/validators/form-validator.test.ts +188 -0
- package/src/validators/form-validator.ts +95 -0
- package/src/validators/js-expression-validator.test.ts +118 -0
- package/src/validators/js-expression-validator.ts +44 -0
- package/src/validators/schema.ts +34 -0
- package/src/zscore/bfa_boys_5_above.json +2522 -0
- package/src/zscore/bfa_girls_5_above.json +2522 -0
- package/src/zscore/hfa_boys_5_above.json +2186 -0
- package/src/zscore/hfa_boys_below5.json +22286 -0
- package/src/zscore/hfa_girls_5_above.json +2186 -0
- package/src/zscore/hfa_girls_below5.json +22286 -0
- package/src/zscore/wfl_boys_below5.json +7814 -0
- package/src/zscore/wfl_girls_below5.json +7814 -0
- package/src/zscore-tests/bmi-age.test.tsx +88 -0
- package/src/zscore-tests/height-age.test.tsx +96 -0
- package/src/zscore-tests/weight-height.test.tsx +87 -0
- package/tools/i18next-parser.config.js +93 -0
- package/translations/en.json +47 -0
- package/translations/es.json +38 -0
- package/translations/fr.json +38 -0
- package/translations/km.json +38 -0
- package/tsconfig.json +19 -0
- package/turbo.json +15 -0
- package/webpack.config.js +1 -0
@@ -0,0 +1,54 @@
|
|
1
|
+
export async function findNumberInput(screen, name: string): Promise<HTMLInputElement> {
|
2
|
+
return await screen.findByRole('spinbutton', { name });
|
3
|
+
}
|
4
|
+
|
5
|
+
export async function findTextOrDateInput(screen, name: string): Promise<HTMLInputElement> {
|
6
|
+
return await screen.findByRole('textbox', { name });
|
7
|
+
}
|
8
|
+
|
9
|
+
export async function findRadioGroupInput(screen, name: string): Promise<HTMLInputElement> {
|
10
|
+
return await screen.findByRole('group', { name });
|
11
|
+
}
|
12
|
+
|
13
|
+
export async function findRadioGroupMember(screen, name: string): Promise<HTMLInputElement> {
|
14
|
+
return await screen.findByRole('radio', { name });
|
15
|
+
}
|
16
|
+
|
17
|
+
export async function findMultiSelectInput(screen, nameSubstring: string): Promise<HTMLInputElement> {
|
18
|
+
return await screen.findByRole('combobox', { name: new RegExp(nameSubstring, 'i') });
|
19
|
+
}
|
20
|
+
|
21
|
+
export async function findSelectInput(screen, name: string): Promise<HTMLInputElement> {
|
22
|
+
return await screen.findByRole('combobox', { name });
|
23
|
+
}
|
24
|
+
|
25
|
+
export async function findAllRadioGroupInputs(screen, name: string): Promise<Array<HTMLInputElement>> {
|
26
|
+
return await screen.queryAllByRole('group', { name });
|
27
|
+
}
|
28
|
+
|
29
|
+
export async function findAllRadioGroupMembers(screen, name: string): Promise<Array<HTMLInputElement>> {
|
30
|
+
return await screen.queryAllByRole('radio', { name });
|
31
|
+
}
|
32
|
+
|
33
|
+
export async function findAllTextOrDateInputs(screen, name: string): Promise<Array<HTMLInputElement>> {
|
34
|
+
return await screen.queryAllByRole('textbox', { name });
|
35
|
+
}
|
36
|
+
|
37
|
+
const fieldTypeToGetterMap = {
|
38
|
+
text: findTextOrDateInput,
|
39
|
+
date: findTextOrDateInput,
|
40
|
+
number: findNumberInput,
|
41
|
+
radio: findRadioGroupInput,
|
42
|
+
'radio-group': findRadioGroupInput,
|
43
|
+
'radio-item': findRadioGroupMember,
|
44
|
+
textarea: findTextOrDateInput,
|
45
|
+
combobox: findMultiSelectInput,
|
46
|
+
select: findSelectInput,
|
47
|
+
};
|
48
|
+
|
49
|
+
export async function assertFormHasAllFields(screen, fields: Array<{ fieldName: string; fieldType: string }>) {
|
50
|
+
const fieldsInDom = await Promise.all(
|
51
|
+
fields.map(({ fieldName, fieldType }) => fieldTypeToGetterMap[fieldType](screen, fieldName)),
|
52
|
+
);
|
53
|
+
await Promise.all(fieldsInDom.map((field) => expect(field).toBeInTheDocument()));
|
54
|
+
}
|
@@ -0,0 +1,59 @@
|
|
1
|
+
import filter from 'lodash/filter';
|
2
|
+
import bfaMale5Above from '../zscore/bfa_boys_5_above.json';
|
3
|
+
import wflMaleBelow5 from '../zscore/wfl_boys_below5.json';
|
4
|
+
import hfaMale5Above from '../zscore/hfa_boys_5_above.json';
|
5
|
+
import hfaMaleBelow5 from '../zscore/hfa_boys_below5.json';
|
6
|
+
import bfaFemale5Above from '../zscore/bfa_girls_5_above.json';
|
7
|
+
import wflFemaleBelow5 from '../zscore/wfl_girls_below5.json';
|
8
|
+
import hfaFemale5Above from '../zscore/hfa_girls_5_above.json';
|
9
|
+
import hfaFemaleBelow5 from '../zscore/hfa_girls_below5.json';
|
10
|
+
import dayjs from 'dayjs';
|
11
|
+
|
12
|
+
export function getZRefByGenderAndAge(gender, birthDate, refdate) {
|
13
|
+
const scoreRefModel = {
|
14
|
+
weightForHeightRef: null,
|
15
|
+
heightForAgeRef: null,
|
16
|
+
bmiForAgeRef: null,
|
17
|
+
};
|
18
|
+
const age = getAge(birthDate, refdate, 'years');
|
19
|
+
const ageInMonths = getAge(birthDate, refdate, 'months');
|
20
|
+
const ageInDays = getAge(birthDate, refdate, 'days');
|
21
|
+
|
22
|
+
if (gender === 'F') {
|
23
|
+
if (age < 5) {
|
24
|
+
scoreRefModel.weightForHeightRef = wflFemaleBelow5;
|
25
|
+
scoreRefModel.heightForAgeRef = getScoreReference(hfaFemaleBelow5, 'Day', ageInDays);
|
26
|
+
}
|
27
|
+
if (age >= 5 && age < 18) {
|
28
|
+
scoreRefModel.bmiForAgeRef = getScoreReference(bfaFemale5Above, 'Month', ageInMonths);
|
29
|
+
scoreRefModel.heightForAgeRef = getScoreReference(hfaFemale5Above, 'Month', ageInMonths);
|
30
|
+
}
|
31
|
+
}
|
32
|
+
if (gender === 'M') {
|
33
|
+
if (age < 5) {
|
34
|
+
scoreRefModel.weightForHeightRef = wflMaleBelow5;
|
35
|
+
scoreRefModel.heightForAgeRef = getScoreReference(hfaMaleBelow5, 'Day', ageInDays);
|
36
|
+
}
|
37
|
+
|
38
|
+
if (age >= 5 && age < 18) {
|
39
|
+
scoreRefModel.bmiForAgeRef = getScoreReference(bfaMale5Above, 'Month', ageInMonths);
|
40
|
+
scoreRefModel.heightForAgeRef = getScoreReference(hfaMale5Above, 'Month', ageInMonths);
|
41
|
+
}
|
42
|
+
}
|
43
|
+
return scoreRefModel;
|
44
|
+
}
|
45
|
+
|
46
|
+
function getScoreReference(refData, searchKey, searchValue): any {
|
47
|
+
return filter(refData, (refObject) => {
|
48
|
+
return refObject[searchKey] === searchValue;
|
49
|
+
});
|
50
|
+
}
|
51
|
+
|
52
|
+
function getAge(birthdate, refDate, ageIn) {
|
53
|
+
if (birthdate && refDate && ageIn) {
|
54
|
+
const todayMoment: any = dayjs(refDate);
|
55
|
+
const birthDateMoment: any = dayjs(birthdate);
|
56
|
+
return todayMoment.diff(birthDateMoment, ageIn);
|
57
|
+
}
|
58
|
+
return null;
|
59
|
+
}
|
@@ -0,0 +1,61 @@
|
|
1
|
+
import { conditionalAnsweredValidator } from './conditional-answered-validator';
|
2
|
+
import { isEmpty } from '../validators/form-validator';
|
3
|
+
import { type FormField, FormFieldValidator } from '../types';
|
4
|
+
|
5
|
+
jest.mock('../validators/form-validator', () => ({
|
6
|
+
isEmpty: jest.fn(),
|
7
|
+
}));
|
8
|
+
|
9
|
+
describe('conditionalAnsweredValidator', () => {
|
10
|
+
let field: FormField;
|
11
|
+
let value: unknown;
|
12
|
+
let config: any;
|
13
|
+
|
14
|
+
beforeEach(() => {
|
15
|
+
field = {
|
16
|
+
label: 'Test Field',
|
17
|
+
type: 'obs',
|
18
|
+
questionOptions: { rendering: 'repeating', concept: 'j8b6705b-b6d8-4eju-8f37-0b14f5347569' },
|
19
|
+
id: 'testFieldId',
|
20
|
+
} as FormField;
|
21
|
+
|
22
|
+
config = {
|
23
|
+
referenceQuestionId: 'referenceQuestionId',
|
24
|
+
referenceQuestionAnswers: ['answer1', 'answer2'],
|
25
|
+
values: { referenceQuestionId: 'answer1' },
|
26
|
+
fields: [{ id: 'referenceQuestionId' }],
|
27
|
+
message: 'Invalid value selected',
|
28
|
+
};
|
29
|
+
});
|
30
|
+
|
31
|
+
it('should return no error if value is empty', () => {
|
32
|
+
(isEmpty as jest.Mock).mockReturnValue(true);
|
33
|
+
value = '';
|
34
|
+
|
35
|
+
const result = conditionalAnsweredValidator.validate(field, value, config);
|
36
|
+
|
37
|
+
expect(result).toEqual([]);
|
38
|
+
});
|
39
|
+
|
40
|
+
it('should return no error if value is not empty and reference question answer is included', () => {
|
41
|
+
(isEmpty as jest.Mock).mockReturnValue(false);
|
42
|
+
value = 'some value';
|
43
|
+
config.values.referenceQuestionId = 'answer1';
|
44
|
+
|
45
|
+
const result = conditionalAnsweredValidator.validate(field, value, config);
|
46
|
+
|
47
|
+
expect(result).toEqual([]);
|
48
|
+
});
|
49
|
+
|
50
|
+
it('should return an error if value is not empty and reference question answer is not included', () => {
|
51
|
+
(isEmpty as jest.Mock).mockReturnValue(false);
|
52
|
+
value = 'some value';
|
53
|
+
config.values.referenceQuestionId = 'answer3';
|
54
|
+
|
55
|
+
const result = conditionalAnsweredValidator.validate(field, value, config);
|
56
|
+
|
57
|
+
expect(result).toEqual([
|
58
|
+
{ resultType: 'error', errCode: 'invalid.valueSelected', message: 'Invalid value selected' },
|
59
|
+
]);
|
60
|
+
});
|
61
|
+
});
|
@@ -0,0 +1,17 @@
|
|
1
|
+
import { isEmpty } from '../validators/form-validator';
|
2
|
+
import { type FormFieldValidator, type FormField } from '../types';
|
3
|
+
|
4
|
+
export const conditionalAnsweredValidator: FormFieldValidator = {
|
5
|
+
validate: function (field: FormField, value: unknown, config: Record<string, any>) {
|
6
|
+
const { referenceQuestionId, referenceQuestionAnswers, values, fields, message } = config;
|
7
|
+
|
8
|
+
const referencedField = fields.find((field) => field.id === referenceQuestionId);
|
9
|
+
const referencedFieldValue = values[referencedField.id];
|
10
|
+
|
11
|
+
if (!isEmpty(value) && !referenceQuestionAnswers.includes(referencedFieldValue)) {
|
12
|
+
return [{ resultType: 'error', errCode: 'invalid.valueSelected', message: message }];
|
13
|
+
}
|
14
|
+
|
15
|
+
return [];
|
16
|
+
},
|
17
|
+
};
|
@@ -0,0 +1,46 @@
|
|
1
|
+
import { type FormField } from '../types';
|
2
|
+
import { DateValidator } from './date-validator';
|
3
|
+
|
4
|
+
describe('DateValidator - validate', () => {
|
5
|
+
const field: FormField = {
|
6
|
+
label: 'HTS Date',
|
7
|
+
type: 'obs',
|
8
|
+
questionOptions: {
|
9
|
+
rendering: 'date',
|
10
|
+
concept: 'j8b6705b-b6d8-4eju-8f37-0b14f5347569',
|
11
|
+
},
|
12
|
+
isRequired: true,
|
13
|
+
required: true,
|
14
|
+
validators: [
|
15
|
+
{
|
16
|
+
type: 'date',
|
17
|
+
allowFutureDates: 'false',
|
18
|
+
},
|
19
|
+
],
|
20
|
+
meta: {},
|
21
|
+
id: 'hts-date',
|
22
|
+
};
|
23
|
+
|
24
|
+
it('should reject if an empty date is provided for a required field', () => {
|
25
|
+
// setup and replay
|
26
|
+
const errors = DateValidator.validate(field, null, field.validators[0]);
|
27
|
+
// verify
|
28
|
+
expect(errors).toEqual([{ errCode: 'field.required', message: 'Field is mandatory', resultType: 'error' }]);
|
29
|
+
});
|
30
|
+
|
31
|
+
it('should by default reject future dates', () => {
|
32
|
+
// setup and replay
|
33
|
+
const errors = DateValidator.validate(field, new Date('December 17, 2032 03:24:00'), null);
|
34
|
+
// verify
|
35
|
+
expect(errors).toEqual([{ errCode: 'value.invalid', message: 'Future dates not allowed', resultType: 'error' }]);
|
36
|
+
});
|
37
|
+
|
38
|
+
it('should accept future dates for fields supporting them', () => {
|
39
|
+
// setup and replay
|
40
|
+
const errors = DateValidator.validate(field, new Date('December 17, 2032 03:24:00'), {
|
41
|
+
allowFutureDates: true,
|
42
|
+
});
|
43
|
+
// verify
|
44
|
+
expect(errors).toEqual([]);
|
45
|
+
});
|
46
|
+
});
|
@@ -0,0 +1,19 @@
|
|
1
|
+
import { type FormFieldValidator, type FormField } from '../types';
|
2
|
+
import { isTrue } from '../utils/boolean-utils';
|
3
|
+
import { FieldValidator } from './form-validator';
|
4
|
+
|
5
|
+
export const DateValidator: FormFieldValidator = {
|
6
|
+
validate: (field: FormField, value: Date, config: any) => {
|
7
|
+
const now = new Date();
|
8
|
+
const errors = !value ? FieldValidator.validate(field, value) : [];
|
9
|
+
if (errors.length) {
|
10
|
+
return errors;
|
11
|
+
}
|
12
|
+
if (value && !isTrue(config?.allowFutureDates)) {
|
13
|
+
return value.getTime && value.getTime() > now.getTime()
|
14
|
+
? [{ resultType: 'error', errCode: 'value.invalid', message: 'Future dates not allowed' }]
|
15
|
+
: [];
|
16
|
+
}
|
17
|
+
return [];
|
18
|
+
},
|
19
|
+
};
|
@@ -0,0 +1,90 @@
|
|
1
|
+
import { type FormField } from '../types';
|
2
|
+
import { DefaultValueValidator } from './default-value-validator';
|
3
|
+
|
4
|
+
describe('DefaultValueValidator - validate', () => {
|
5
|
+
// setup
|
6
|
+
const codedField: FormField = {
|
7
|
+
label: 'Past enrolled patient programs',
|
8
|
+
type: 'obs',
|
9
|
+
questionOptions: {
|
10
|
+
rendering: 'checkbox',
|
11
|
+
concept: '3hbkj9-b6d8-4eju-8f37-0b14f5347jv9',
|
12
|
+
answers: [
|
13
|
+
{ label: 'Oncology Screening and Diagnosis Program', concept: '105e7ad6-c1fd-11eb-8529-0242ac130ju9' },
|
14
|
+
{ label: 'Fight Malaria Initiative', concept: '305e7ad6-c1fd-11eb-8529-0242ac130003' },
|
15
|
+
],
|
16
|
+
},
|
17
|
+
id: 'past-patient-programs',
|
18
|
+
};
|
19
|
+
const dateField: FormField = {
|
20
|
+
label: 'HTS Date',
|
21
|
+
type: 'obs',
|
22
|
+
questionOptions: {
|
23
|
+
rendering: 'date',
|
24
|
+
concept: 'j8b6705b-b6d8-4eju-8f37-0b14f5347569',
|
25
|
+
},
|
26
|
+
id: 'hts-date',
|
27
|
+
};
|
28
|
+
const numericField: FormField = {
|
29
|
+
label: 'Temperature',
|
30
|
+
type: 'obs',
|
31
|
+
questionOptions: {
|
32
|
+
rendering: 'number',
|
33
|
+
concept: '2c43u05b-b6d8-4eju-8f37-0b14f5347560',
|
34
|
+
},
|
35
|
+
id: 'temperature',
|
36
|
+
};
|
37
|
+
it('should accept valid values for coded types', () => {
|
38
|
+
// setup and replay
|
39
|
+
const errors = DefaultValueValidator.validate(codedField, '105e7ad6-c1fd-11eb-8529-0242ac130ju9');
|
40
|
+
|
41
|
+
// verify
|
42
|
+
expect(errors).toEqual([]);
|
43
|
+
});
|
44
|
+
|
45
|
+
it('should reject invalid values for coded types', () => {
|
46
|
+
// setup and replay
|
47
|
+
const errors = DefaultValueValidator.validate(codedField, 'some none existing value');
|
48
|
+
|
49
|
+
// verify
|
50
|
+
expect(errors).toEqual([
|
51
|
+
{ errCode: 'invalid.defaultValue', message: 'Value not found in coded answers list', resultType: 'error' },
|
52
|
+
]);
|
53
|
+
});
|
54
|
+
|
55
|
+
it('should accept valid date values', () => {
|
56
|
+
// setup and replay
|
57
|
+
const errors = DefaultValueValidator.validate(dateField, '2020-01-20');
|
58
|
+
|
59
|
+
// verify
|
60
|
+
expect(errors).toEqual([]);
|
61
|
+
});
|
62
|
+
|
63
|
+
it('should reject invalid date values', () => {
|
64
|
+
// setup and replay
|
65
|
+
const errors = DefaultValueValidator.validate(dateField, 'test date');
|
66
|
+
|
67
|
+
// verify
|
68
|
+
expect(errors).toEqual([
|
69
|
+
{ errCode: 'invalid.defaultValue', message: `Invalid date value: 'test date'`, resultType: 'error' },
|
70
|
+
]);
|
71
|
+
});
|
72
|
+
|
73
|
+
it('should accept valid numeric values', () => {
|
74
|
+
// setup and replay
|
75
|
+
const errors = DefaultValueValidator.validate(numericField, '500.5');
|
76
|
+
|
77
|
+
// verify
|
78
|
+
expect(errors).toEqual([]);
|
79
|
+
});
|
80
|
+
|
81
|
+
it('should reject invalid numeric values', () => {
|
82
|
+
// setup and replay
|
83
|
+
const errors = DefaultValueValidator.validate(numericField, '500.5hds');
|
84
|
+
|
85
|
+
// verify
|
86
|
+
expect(errors).toEqual([
|
87
|
+
{ errCode: 'invalid.defaultValue', message: `Invalid numerical value: '500.5hds'`, resultType: 'error' },
|
88
|
+
]);
|
89
|
+
});
|
90
|
+
});
|
@@ -0,0 +1,36 @@
|
|
1
|
+
import dayjs from 'dayjs';
|
2
|
+
import { type FormFieldValidator, type FormField } from '../types';
|
3
|
+
import { codedTypes } from '../constants';
|
4
|
+
import { isEmpty } from './form-validator';
|
5
|
+
|
6
|
+
export const DefaultValueValidator: FormFieldValidator = {
|
7
|
+
validate: (field: FormField, value: any) => {
|
8
|
+
if (!isEmpty(value) && codedTypes.includes(field.questionOptions.rendering)) {
|
9
|
+
const valuesArray = Array.isArray(value) ? value : [value];
|
10
|
+
// check whether value exists in answers
|
11
|
+
if (
|
12
|
+
!valuesArray.every((val: string) =>
|
13
|
+
field.questionOptions.answers?.find((answer) => answer.concept === val || answer.value === val),
|
14
|
+
)
|
15
|
+
) {
|
16
|
+
return [
|
17
|
+
{ resultType: 'error', errCode: 'invalid.defaultValue', message: 'Value not found in coded answers list' },
|
18
|
+
];
|
19
|
+
}
|
20
|
+
}
|
21
|
+
if (!isEmpty(value) && field.questionOptions.rendering == 'date') {
|
22
|
+
// Check if value is a valid date value
|
23
|
+
if (!dayjs(value).isValid()) {
|
24
|
+
return [{ resultType: 'error', errCode: 'invalid.defaultValue', message: `Invalid date value: '${value}'` }];
|
25
|
+
}
|
26
|
+
}
|
27
|
+
if (!isEmpty(value) && field.questionOptions.rendering == 'number') {
|
28
|
+
if (isNaN(value)) {
|
29
|
+
return [
|
30
|
+
{ resultType: 'error', errCode: 'invalid.defaultValue', message: `Invalid numerical value: '${value}'` },
|
31
|
+
];
|
32
|
+
}
|
33
|
+
}
|
34
|
+
return [];
|
35
|
+
},
|
36
|
+
};
|
@@ -0,0 +1,188 @@
|
|
1
|
+
import { type FormField } from '../types';
|
2
|
+
import { FieldValidator } from './form-validator';
|
3
|
+
|
4
|
+
|
5
|
+
describe('FieldValidator - validate', () => {
|
6
|
+
const numberInputField: FormField = {
|
7
|
+
label: 'A Question of type obs that renders a Number input',
|
8
|
+
type: 'obs',
|
9
|
+
questionOptions: {
|
10
|
+
rendering: 'number',
|
11
|
+
concept: 'a-system-defined-concept-uuid',
|
12
|
+
min: '5',
|
13
|
+
max: '10',
|
14
|
+
},
|
15
|
+
meta: {},
|
16
|
+
id: 'sampleNumberQuestion',
|
17
|
+
};
|
18
|
+
|
19
|
+
const numberWithDecimalsInputField: FormField = {
|
20
|
+
label: 'A Question of type obs that renders a Number input',
|
21
|
+
type: 'obs',
|
22
|
+
questionOptions: {
|
23
|
+
rendering: 'number',
|
24
|
+
concept: 'a-system-defined-concept-uuid',
|
25
|
+
disallowDecimals: true,
|
26
|
+
min: '5.0',
|
27
|
+
max: '10.0',
|
28
|
+
},
|
29
|
+
id: 'sampleNumberWithDecimalsQuestion',
|
30
|
+
};
|
31
|
+
|
32
|
+
const textInputField: FormField = {
|
33
|
+
label: 'A Question of type obs that renders a Text input',
|
34
|
+
type: 'obs',
|
35
|
+
questionOptions: {
|
36
|
+
rendering: 'text',
|
37
|
+
concept: 'a-system-defined-concept-uuid',
|
38
|
+
minLength: '5',
|
39
|
+
maxLength: '10',
|
40
|
+
},
|
41
|
+
meta: {},
|
42
|
+
id: 'sampleTextQuestion',
|
43
|
+
};
|
44
|
+
|
45
|
+
const textInputFieldWithoutValidation: FormField = {
|
46
|
+
label: 'A Question of type obs that renders a Text input',
|
47
|
+
type: 'obs',
|
48
|
+
questionOptions: {
|
49
|
+
rendering: 'text',
|
50
|
+
concept: 'a-system-defined-concept-uuid',
|
51
|
+
},
|
52
|
+
meta: {},
|
53
|
+
id: 'sampleTextQuestion',
|
54
|
+
};
|
55
|
+
|
56
|
+
const textInputFieldWithMinValidation: FormField = {
|
57
|
+
label: 'A Question of type obs that renders a Text input',
|
58
|
+
type: 'obs',
|
59
|
+
questionOptions: {
|
60
|
+
rendering: 'text',
|
61
|
+
concept: 'a-system-defined-concept-uuid',
|
62
|
+
minLength: '5',
|
63
|
+
},
|
64
|
+
meta: {},
|
65
|
+
id: 'sampleTextQuestion',
|
66
|
+
};
|
67
|
+
|
68
|
+
const textInputFieldWithMaxValidation: FormField = {
|
69
|
+
label: 'A Question of type obs that renders a Text input',
|
70
|
+
type: 'obs',
|
71
|
+
questionOptions: {
|
72
|
+
rendering: 'text',
|
73
|
+
concept: 'a-system-defined-concept-uuid',
|
74
|
+
maxLength: '10',
|
75
|
+
},
|
76
|
+
meta: {},
|
77
|
+
id: 'sampleTextQuestion',
|
78
|
+
};
|
79
|
+
|
80
|
+
it('should fail on wrong max length only for inputText', () => {
|
81
|
+
const validationErrors = FieldValidator.validate(textInputFieldWithMaxValidation, 'super long text to test', null);
|
82
|
+
|
83
|
+
expect(validationErrors).toEqual([
|
84
|
+
{
|
85
|
+
errCode: 'field.outOfBound',
|
86
|
+
message: `Length should not exceed ${textInputField.questionOptions.maxLength} characters`,
|
87
|
+
resultType: 'error',
|
88
|
+
},
|
89
|
+
]);
|
90
|
+
});
|
91
|
+
|
92
|
+
it('should fail on wrong min length only for inputText', () => {
|
93
|
+
const validationErrors = FieldValidator.validate(textInputFieldWithMinValidation, 'sup', null);
|
94
|
+
|
95
|
+
expect(validationErrors).toEqual([
|
96
|
+
{
|
97
|
+
errCode: 'field.outOfBound',
|
98
|
+
message: `Length should be at least ${textInputField.questionOptions.minLength} characters`,
|
99
|
+
resultType: 'error',
|
100
|
+
},
|
101
|
+
]);
|
102
|
+
});
|
103
|
+
|
104
|
+
it('should not fail if min and max is not defined for inputText', () => {
|
105
|
+
const validationErrors = FieldValidator.validate(
|
106
|
+
textInputFieldWithoutValidation,
|
107
|
+
'super text super text super text',
|
108
|
+
null
|
109
|
+
);
|
110
|
+
|
111
|
+
expect(validationErrors).toEqual([]);
|
112
|
+
});
|
113
|
+
|
114
|
+
it('should fail for text length greater than the max defined length', () => {
|
115
|
+
const validationErrors = FieldValidator.validate(textInputField, 'super text super text super text', null);
|
116
|
+
|
117
|
+
expect(validationErrors).toEqual([
|
118
|
+
{
|
119
|
+
errCode: 'field.outOfBound',
|
120
|
+
message: `Length should not exceed ${textInputField.questionOptions.maxLength} characters`,
|
121
|
+
resultType: 'error',
|
122
|
+
},
|
123
|
+
]);
|
124
|
+
});
|
125
|
+
|
126
|
+
it('should fail for text length lesser than the min defined length', () => {
|
127
|
+
const validationErrors = FieldValidator.validate(textInputField, 'text', null);
|
128
|
+
|
129
|
+
expect(validationErrors).toEqual([
|
130
|
+
{
|
131
|
+
errCode: 'field.outOfBound',
|
132
|
+
message: `Length should be at least ${textInputField.questionOptions.minLength} characters`,
|
133
|
+
resultType: 'error',
|
134
|
+
},
|
135
|
+
]);
|
136
|
+
});
|
137
|
+
|
138
|
+
it('should accept value with length within the defined range', () => {
|
139
|
+
const validationErrors = FieldValidator.validate(textInputField, 'qwertyu', null);
|
140
|
+
expect(validationErrors).toEqual([]);
|
141
|
+
});
|
142
|
+
|
143
|
+
it('should accept value with length equal to minLength', () => {
|
144
|
+
const validationErrors = FieldValidator.validate(textInputField, 'qwert', null);
|
145
|
+
expect(validationErrors).toEqual([]);
|
146
|
+
});
|
147
|
+
|
148
|
+
it('should accept value with length equal to maxLength', () => {
|
149
|
+
const validationErrors = FieldValidator.validate(textInputField, 'qwertyuiop', null);
|
150
|
+
expect(validationErrors).toEqual([]);
|
151
|
+
});
|
152
|
+
|
153
|
+
// Number Input Validator Tests
|
154
|
+
it('should fail for number lesser than the defined min allowed', () => {
|
155
|
+
const validationErrors = FieldValidator.validate(numberInputField, 3, null);
|
156
|
+
|
157
|
+
expect(validationErrors).toEqual([
|
158
|
+
{
|
159
|
+
errCode: 'field.outOfBound',
|
160
|
+
message: `Value must be greater than ${numberInputField.questionOptions.min}`,
|
161
|
+
resultType: 'error',
|
162
|
+
},
|
163
|
+
]);
|
164
|
+
});
|
165
|
+
|
166
|
+
it('should fail for numbers greater than the defined max allowed', () => {
|
167
|
+
const validationErrors = FieldValidator.validate(numberInputField, 100, null);
|
168
|
+
|
169
|
+
expect(validationErrors).toEqual([
|
170
|
+
{
|
171
|
+
errCode: 'field.outOfBound',
|
172
|
+
message: `Value must be lower than ${numberInputField.questionOptions.max}`,
|
173
|
+
resultType: 'error',
|
174
|
+
},
|
175
|
+
]);
|
176
|
+
});
|
177
|
+
|
178
|
+
it('should fail for numbers with decimals', () => {
|
179
|
+
const validationErrors = FieldValidator.validate(numberWithDecimalsInputField, 8.5, null);
|
180
|
+
expect(validationErrors).toEqual([
|
181
|
+
{
|
182
|
+
errCode: 'field.outOfBound',
|
183
|
+
message: 'Decimal values are not allowed for this field',
|
184
|
+
resultType: 'error',
|
185
|
+
},
|
186
|
+
]);
|
187
|
+
});
|
188
|
+
});
|
@@ -0,0 +1,95 @@
|
|
1
|
+
import { type FormFieldValidator, type FormField } from '../types';
|
2
|
+
export const fieldRequiredErrCode = 'field.required';
|
3
|
+
export const fieldOutOfBoundErrCode = 'field.outOfBound';
|
4
|
+
|
5
|
+
export const FieldValidator: FormFieldValidator = {
|
6
|
+
validate: (field: FormField, value: any) => {
|
7
|
+
if (field.meta?.submission?.unspecified) {
|
8
|
+
return [];
|
9
|
+
}
|
10
|
+
if (isEmpty(value) && field.isRequired) {
|
11
|
+
if (typeof field.required === 'object' && field.required.type === 'conditionalRequired' && field.isRequired) {
|
12
|
+
return addError(fieldRequiredErrCode, field.required.message ?? 'Field is mandatory');
|
13
|
+
} else if (field.isRequired) {
|
14
|
+
return addError(fieldRequiredErrCode, 'Field is mandatory');
|
15
|
+
}
|
16
|
+
}
|
17
|
+
if (field.questionOptions.rendering === 'text') {
|
18
|
+
const minLength = field.questionOptions.minLength;
|
19
|
+
const maxLength = field.questionOptions.maxLength;
|
20
|
+
|
21
|
+
return textInputLengthValidator(minLength, maxLength, value?.length) ?? [];
|
22
|
+
}
|
23
|
+
if (field.questionOptions.rendering === 'number') {
|
24
|
+
const min = Number(field.questionOptions.min);
|
25
|
+
const max = Number(field.questionOptions.max);
|
26
|
+
const disallowDecimals = field.questionOptions.disallowDecimals || field.meta?.concept?.allowDecimal;
|
27
|
+
if (isEmpty(value)) return [];
|
28
|
+
if (disallowDecimals && !Number.isInteger(value)) {
|
29
|
+
return addError(fieldOutOfBoundErrCode, 'Decimal values are not allowed for this field');
|
30
|
+
}
|
31
|
+
if (!Number.isNaN(min) || !Number.isNaN(max)) {
|
32
|
+
return numberInputRangeValidator(min, max, value);
|
33
|
+
}
|
34
|
+
}
|
35
|
+
return [];
|
36
|
+
},
|
37
|
+
};
|
38
|
+
|
39
|
+
export function numberInputRangeValidator(min: number, max: number, inputValue: number) {
|
40
|
+
if (!Number.isNaN(min) && inputValue < min) {
|
41
|
+
return addError(fieldOutOfBoundErrCode, `Value must be greater than ${min}`);
|
42
|
+
}
|
43
|
+
|
44
|
+
if (!Number.isNaN(max) && inputValue > max) {
|
45
|
+
return addError(fieldOutOfBoundErrCode, `Value must be lower than ${max}`);
|
46
|
+
}
|
47
|
+
|
48
|
+
return [];
|
49
|
+
}
|
50
|
+
|
51
|
+
export function isEmpty(value: any): boolean {
|
52
|
+
if (value === undefined || value === null || value === '') {
|
53
|
+
return true;
|
54
|
+
}
|
55
|
+
if (typeof value == 'string' && !value?.trim()) {
|
56
|
+
return true;
|
57
|
+
}
|
58
|
+
if (Array.isArray(value) && !value.length) {
|
59
|
+
return true;
|
60
|
+
}
|
61
|
+
return false;
|
62
|
+
}
|
63
|
+
|
64
|
+
export function textInputLengthValidator(minLength: string, maxLength: string, inputLength: number) {
|
65
|
+
const minLen = Number(minLength);
|
66
|
+
const maxLen = Number(maxLength);
|
67
|
+
|
68
|
+
if (typeof inputLength === 'number' && !Number.isNaN(inputLength)) {
|
69
|
+
if (minLen && maxLen && inputLength >= minLen && inputLength <= maxLen) {
|
70
|
+
return [];
|
71
|
+
}
|
72
|
+
|
73
|
+
if (minLen && inputLength < minLen) {
|
74
|
+
return addError(fieldOutOfBoundErrCode, `Length should be at least ${minLen} characters`);
|
75
|
+
}
|
76
|
+
|
77
|
+
if (maxLen && inputLength > maxLen) {
|
78
|
+
return addError(fieldOutOfBoundErrCode, `Length should not exceed ${maxLen} characters`);
|
79
|
+
}
|
80
|
+
|
81
|
+
if (maxLen && minLen && inputLength < minLen && inputLength > maxLen) {
|
82
|
+
return addError(fieldOutOfBoundErrCode, `Length should be between ${minLen} and ${maxLen} characters`);
|
83
|
+
}
|
84
|
+
}
|
85
|
+
}
|
86
|
+
|
87
|
+
export function addError(errorCode: string, message: string): [{}] {
|
88
|
+
return [
|
89
|
+
{
|
90
|
+
resultType: 'error',
|
91
|
+
errCode: errorCode,
|
92
|
+
message: message,
|
93
|
+
},
|
94
|
+
];
|
95
|
+
}
|