Skip to content

Commit

Permalink
Merge pull request #365 from alexlipovka/informative-field-validation…
Browse files Browse the repository at this point in the history
…-error-display

Display more relevant validation errors on BaseQuestionnaireResponseForm
  • Loading branch information
ir4y authored Nov 15, 2024
2 parents c4b82c4 + a8fce6c commit 256fdcd
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { ControllerFieldState, ControllerRenderProps, FieldValues } from 'react-hook-form';
import { describe } from 'vitest';

import { getFieldErrorMessage } from 'src/components/BaseQuestionnaireResponseForm/utils';

const FIELD_STATE_MESSAGE_MAP = [
{
fieldName: 'first-name.0.value.string',
fieldValue: 'NotSoLongReallyName',
fieldText: 'First name',
errorMessage: 'first-name[0].value.string must be at most 30 characters',
errorType: 'max',
expectedResult: 'First name must be at most 30 characters',
},
{
fieldName: 'first-name.0.value.string',
fieldValue: 'Basic - 1',
fieldText: 'First name',
errorMessage: 'first-name[0].value.string is a required field',
errorType: 'required',
expectedResult: 'First name is a required field',
},
];

describe('Field error message should be relevant and human readable', () => {
test.each(FIELD_STATE_MESSAGE_MAP)(
'getFieldErrorMessage returns correctly transformed message',
(fieldStateMessage) => {
const field: ControllerRenderProps<FieldValues, any> = {
name: fieldStateMessage.fieldName,
value: fieldStateMessage.fieldValue,
onChange: () => {
return void 0;
},
onBlur: () => {
return void 0;
},
ref: () => {
return void 0;
},
};

const fieldState: ControllerFieldState = {
error: {
message: fieldStateMessage.errorMessage,
type: fieldStateMessage.errorType,
},
invalid: true,
isTouched: true,
isDirty: true,
};

const fieldText = fieldStateMessage.fieldText;

const invalidFieldMessage = getFieldErrorMessage(field, fieldState, fieldText);

expect(invalidFieldMessage).toBe(fieldStateMessage.expectedResult);
},
);
});
6 changes: 5 additions & 1 deletion src/components/BaseQuestionnaireResponseForm/hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { QuestionnaireItem } from '@beda.software/aidbox-types';

import s from './BaseQuestionnaireResponseForm.module.scss';
import { FieldLabel } from './FieldLabel';
import { getFieldErrorMessage } from 'src/components/BaseQuestionnaireResponseForm/utils';

export function useFieldController(fieldName: any, questionItem: QuestionnaireItem) {
const qrfContext = useQuestionnaireResponseFormContext();
Expand All @@ -20,11 +21,14 @@ export function useFieldController(fieldName: any, questionItem: QuestionnaireIt
name: fieldName.join('.'),
...(repeats ? { defaultValue: [] } : {}),
});

const invalidFieldMessage = getFieldErrorMessage(field, fieldState, text);

const formItem: FormItemProps = {
label: <FieldLabel questionItem={questionItem} />,
hidden: hidden,
validateStatus: fieldState?.invalid ? 'error' : 'success',
help: fieldState?.invalid ? `${text} is required` : undefined,
help: invalidFieldMessage,
required,
className: classNames(s.field, {
[s._hidden]: hidden,
Expand Down
21 changes: 21 additions & 0 deletions src/components/BaseQuestionnaireResponseForm/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { ControllerFieldState, ControllerRenderProps, FieldValues } from 'react-hook-form';

export function getFieldErrorMessage(
field: ControllerRenderProps<FieldValues, any>,
fieldState: ControllerFieldState,
text?: string,
) {
if (!fieldState || !fieldState.invalid) {
return undefined;
}

if (!fieldState.error || !fieldState.error.message) {
return undefined;
}
// replace [0], [1] with .0, .1 to match field.name
const errorMessageWithInternalFieldName = fieldState.error.message.replace(/\[(\d+)\]/g, '.$1');

const errorMessageWithHumanReadableFieldName = errorMessageWithInternalFieldName.replace(field.name, text ?? '');

return errorMessageWithHumanReadableFieldName;
}

0 comments on commit 256fdcd

Please sign in to comment.