diff --git a/packages/smart-forms-renderer/src/components/FormComponents/AttachmentItem/AttachmentField.tsx b/packages/smart-forms-renderer/src/components/FormComponents/AttachmentItem/AttachmentField.tsx
index 9ed583873..dde7c199e 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/AttachmentItem/AttachmentField.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/AttachmentItem/AttachmentField.tsx
@@ -35,6 +35,7 @@ interface AttachmentFieldProps extends PropsWithIsTabledAttribute {
itemText: string | undefined;
attachmentValues: AttachmentValues;
feedback: string;
+ feedbackSeverity?: 'error' | 'warning';
readOnly: boolean;
instructionsId: string | undefined;
onUploadFile: (file: File | null) => void;
@@ -49,6 +50,7 @@ function AttachmentField(props: AttachmentFieldProps) {
itemText,
attachmentValues,
feedback,
+ feedbackSeverity,
readOnly,
isTabled,
instructionsId,
@@ -148,7 +150,12 @@ function AttachmentField(props: AttachmentFieldProps) {
- {feedback ? {feedback} : null}
+ {feedback ? (
+
+ {feedback}
+
+ ) : null}
>
);
}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/AttachmentItem/AttachmentFieldWrapper.tsx b/packages/smart-forms-renderer/src/components/FormComponents/AttachmentItem/AttachmentFieldWrapper.tsx
index 9750aae77..5e1f28332 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/AttachmentItem/AttachmentFieldWrapper.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/AttachmentItem/AttachmentFieldWrapper.tsx
@@ -34,6 +34,7 @@ interface AttachmentFieldWrapperProps
qItem: QuestionnaireItem;
attachmentValues: AttachmentValues;
feedback: string;
+ feedbackSeverity?: 'error' | 'warning';
readOnly: boolean;
instructionsId: string | undefined;
onUploadFile: (file: File | null) => void;
@@ -46,6 +47,7 @@ function AttachmentFieldWrapper(props: AttachmentFieldWrapperProps) {
qItem,
attachmentValues,
feedback,
+ feedbackSeverity,
readOnly,
isRepeated,
isTabled,
@@ -65,6 +67,7 @@ function AttachmentFieldWrapper(props: AttachmentFieldWrapperProps) {
itemText={qItem.text}
attachmentValues={attachmentValues}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
readOnly={readOnly}
isTabled={isTabled}
instructionsId={instructionsId}
@@ -92,6 +95,7 @@ function AttachmentFieldWrapper(props: AttachmentFieldWrapperProps) {
itemText={qItem.text}
attachmentValues={attachmentValues}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
readOnly={readOnly}
isTabled={isTabled}
instructionsId={instructionsId}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/AttachmentItem/AttachmentItem.tsx b/packages/smart-forms-renderer/src/components/FormComponents/AttachmentItem/AttachmentItem.tsx
index 34e4086b2..53d90e059 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/AttachmentItem/AttachmentItem.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/AttachmentItem/AttachmentItem.tsx
@@ -26,7 +26,7 @@ import AttachmentFieldWrapper from './AttachmentFieldWrapper';
import { HTML5Backend } from 'react-dnd-html5-backend';
import { DndProvider } from 'react-dnd';
import { createAttachmentAnswer } from '../../../utils/fileUtils';
-import useValidationFeedback from '../../../hooks/useValidationFeedback';
+import useValidationFeedbackSeverity from '../../../hooks/useValidationFeedbackSeverity';
import { getInstructionsId } from '../ItemParts/ItemFieldGrid';
export interface AttachmentValues {
@@ -60,7 +60,7 @@ function AttachmentItem(props: BaseItemProps) {
const readOnly = useReadOnly(qItem, parentIsReadOnly);
// Perform validation checks
- const feedback = useValidationFeedback(qItem, feedbackFromParent);
+ const { feedback, feedbackSeverity } = useValidationFeedbackSeverity(qItem, feedbackFromParent);
// Get instructions ID for aria-describedby
const { displayInstructions } = useRenderingExtensions(qItem);
@@ -114,6 +114,7 @@ function AttachmentItem(props: BaseItemProps) {
qItem={qItem}
attachmentValues={{ uploadedFile: uploadedFile, url: url, fileName: fileName }}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
readOnly={readOnly}
isRepeated={isRepeated}
isTabled={isTabled}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/BooleanItem/BooleanField.tsx b/packages/smart-forms-renderer/src/components/FormComponents/BooleanItem/BooleanField.tsx
index 053bfa821..d3e7eb509 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/BooleanItem/BooleanField.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/BooleanItem/BooleanField.tsx
@@ -37,6 +37,7 @@ interface BooleanFieldProps {
readOnly: boolean;
valueBoolean: boolean | undefined;
feedback: string;
+ feedbackSeverity?: 'error' | 'warning';
calcExpUpdated: boolean;
instructionsId: string | undefined;
onCheckedChange: (newValue: string) => void;
@@ -49,6 +50,7 @@ const BooleanField = memo(function BooleanField(props: BooleanFieldProps) {
readOnly,
valueBoolean,
feedback,
+ feedbackSeverity,
calcExpUpdated,
instructionsId,
onCheckedChange,
@@ -182,7 +184,12 @@ const BooleanField = memo(function BooleanField(props: BooleanFieldProps) {
/>
- {feedback ? {feedback} : null}
+ {feedback ? (
+
+ {feedback}
+
+ ) : null}
>
);
});
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/BooleanItem/BooleanItem.tsx b/packages/smart-forms-renderer/src/components/FormComponents/BooleanItem/BooleanItem.tsx
index 19212fe7b..28956440e 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/BooleanItem/BooleanItem.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/BooleanItem/BooleanItem.tsx
@@ -17,7 +17,7 @@
import Box from '@mui/material/Box';
import useReadOnly from '../../../hooks/useReadOnly';
-import useValidationFeedback from '../../../hooks/useValidationFeedback';
+import useValidationFeedbackSeverity from '../../../hooks/useValidationFeedbackSeverity';
import type { BaseItemProps } from '../../../interfaces/renderProps.interface';
import { useQuestionnaireStore } from '../../../stores';
import { createEmptyQrItem, getQRItemId } from '../../../utils/qrItem';
@@ -44,7 +44,7 @@ function BooleanItem(props: BaseItemProps) {
const readOnly = useReadOnly(qItem, parentIsReadOnly);
// Perform validation checks - there's no string-based input here
- const feedback = useValidationFeedback(qItem, feedbackFromParent);
+ const { feedback, feedbackSeverity } = useValidationFeedbackSeverity(qItem, feedbackFromParent);
// Get instructions ID for aria-describedby
const { displayInstructions } = useRenderingExtensions(qItem);
@@ -90,6 +90,7 @@ function BooleanItem(props: BaseItemProps) {
readOnly={readOnly}
valueBoolean={valueBoolean}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
calcExpUpdated={calcExpUpdated}
instructionsId={instructionsId}
onCheckedChange={handleValueChange}
@@ -106,6 +107,7 @@ function BooleanItem(props: BaseItemProps) {
readOnly={readOnly}
valueBoolean={valueBoolean}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
calcExpUpdated={calcExpUpdated}
instructionsId={instructionsId}
onCheckedChange={handleValueChange}
@@ -130,6 +132,7 @@ function BooleanItem(props: BaseItemProps) {
readOnly={readOnly}
valueBoolean={valueBoolean}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
calcExpUpdated={calcExpUpdated}
instructionsId={instructionsId}
onCheckedChange={handleValueChange}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceAutocompleteItem.tsx b/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceAutocompleteItem.tsx
index 571fbe5c8..2ab2d07ea 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceAutocompleteItem.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceAutocompleteItem.tsx
@@ -22,7 +22,7 @@ import type { AlertColor } from '@mui/material/Alert';
import useDebounce from '../../../hooks/useDebounce';
import useReadOnly from '../../../hooks/useReadOnly';
import useTerminologyServerQuery from '../../../hooks/useTerminologyServerQuery';
-import useValidationFeedback from '../../../hooks/useValidationFeedback';
+import useValidationFeedbackSeverity from '../../../hooks/useValidationFeedbackSeverity';
import type { BaseItemProps } from '../../../interfaces/renderProps.interface';
import { useQuestionnaireStore } from '../../../stores';
import { AUTOCOMPLETE_DEBOUNCE_DURATION } from '../../../utils/debounce';
@@ -64,7 +64,8 @@ function ChoiceAutocompleteItem(props: BaseItemProps) {
const readOnly = useReadOnly(qItem, parentIsReadOnly);
// Perform validation checks
- const validationFeedback = useValidationFeedback(qItem, feedbackFromParent);
+ const { feedback: validationFeedback, feedbackSeverity: validationFeedbackSeverity } =
+ useValidationFeedbackSeverity(qItem, feedbackFromParent);
const { displayInstructions } = renderingExtensions;
const instructionsId =
displayInstructions && !validationFeedback ? `instructions-${qItem.linkId}` : undefined;
@@ -79,7 +80,7 @@ function ChoiceAutocompleteItem(props: BaseItemProps) {
if (terminologyFeedback) {
feedback = terminologyFeedback;
} else if (validationFeedback !== '') {
- feedback = { message: validationFeedback, color: 'error' };
+ feedback = { message: validationFeedback, color: validationFeedbackSeverity };
}
if (!qItem.answerValueSet) {
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceCheckboxAnswerOptionFields.tsx b/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceCheckboxAnswerOptionFields.tsx
index abbd91a68..f0c8a8fd8 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceCheckboxAnswerOptionFields.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceCheckboxAnswerOptionFields.tsx
@@ -27,6 +27,7 @@ interface ChoiceCheckboxAnswerOptionFieldsProps {
options: QuestionnaireItemAnswerOption[];
answers: QuestionnaireResponseItemAnswer[];
feedback: string;
+ feedbackSeverity?: 'error' | 'warning';
readOnly: boolean;
expressionUpdated: boolean;
answerOptionsToggleExpressionsMap: Map;
@@ -42,6 +43,7 @@ function ChoiceCheckboxAnswerOptionFields(props: ChoiceCheckboxAnswerOptionField
options,
answers,
feedback,
+ feedbackSeverity,
readOnly,
expressionUpdated,
answerOptionsToggleExpressionsMap,
@@ -57,6 +59,7 @@ function ChoiceCheckboxAnswerOptionFields(props: ChoiceCheckboxAnswerOptionField
options={options}
answers={answers}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
readOnly={readOnly}
expressionUpdated={expressionUpdated}
answerOptionsToggleExpressionsMap={answerOptionsToggleExpressionsMap}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceCheckboxAnswerOptionItem.tsx b/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceCheckboxAnswerOptionItem.tsx
index 65b551ac7..e6d760b24 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceCheckboxAnswerOptionItem.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceCheckboxAnswerOptionItem.tsx
@@ -17,7 +17,7 @@
import useAnswerOptionsToggleExpressions from '../../../hooks/useAnswerOptionsToggleExpressions';
import useReadOnly from '../../../hooks/useReadOnly';
-import useValidationFeedback from '../../../hooks/useValidationFeedback';
+import useValidationFeedbackSeverity from '../../../hooks/useValidationFeedbackSeverity';
import type { BaseItemProps } from '../../../interfaces/renderProps.interface';
import { useQuestionnaireStore } from '../../../stores';
import { updateChoiceCheckboxAnswers } from '../../../utils/choice';
@@ -57,7 +57,7 @@ function ChoiceCheckboxAnswerOptionItem(props: BaseItemProps) {
const readOnly = useReadOnly(qItem, parentIsReadOnly);
// Perform validation checks
- const feedback = useValidationFeedback(qItem, feedbackFromParent);
+ const { feedback, feedbackSeverity } = useValidationFeedbackSeverity(qItem, feedbackFromParent);
const instructionsId = getInstructionsId(qItem, displayInstructions, !!feedback);
const options = qItem.answerOption ?? [];
@@ -95,6 +95,7 @@ function ChoiceCheckboxAnswerOptionItem(props: BaseItemProps) {
options={options}
answers={answers}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
readOnly={readOnly}
expressionUpdated={calcExpUpdated || answerOptionsToggleExpUpdated}
answerOptionsToggleExpressionsMap={answerOptionsToggleExpressionsMap}
@@ -124,6 +125,7 @@ function ChoiceCheckboxAnswerOptionItem(props: BaseItemProps) {
options={options}
answers={answers}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
readOnly={readOnly}
expressionUpdated={calcExpUpdated || answerOptionsToggleExpUpdated}
answerOptionsToggleExpressionsMap={answerOptionsToggleExpressionsMap}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceCheckboxAnswerValueSetFields.tsx b/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceCheckboxAnswerValueSetFields.tsx
index c248b5050..3ea6eb3f7 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceCheckboxAnswerValueSetFields.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceCheckboxAnswerValueSetFields.tsx
@@ -31,6 +31,7 @@ interface ChoiceCheckboxAnswerValueSetFieldsProps {
options: QuestionnaireItemAnswerOption[];
answers: QuestionnaireResponseItemAnswer[];
feedback: string;
+ feedbackSeverity?: 'error' | 'warning';
readOnly: boolean;
expressionUpdated: boolean;
answerOptionsToggleExpressionsMap: Map;
@@ -47,6 +48,7 @@ function ChoiceCheckboxAnswerValueSetFields(props: ChoiceCheckboxAnswerValueSetF
options,
answers,
feedback,
+ feedbackSeverity,
readOnly,
expressionUpdated,
answerOptionsToggleExpressionsMap,
@@ -64,6 +66,7 @@ function ChoiceCheckboxAnswerValueSetFields(props: ChoiceCheckboxAnswerValueSetF
options={options}
answers={answers}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
readOnly={readOnly}
expressionUpdated={expressionUpdated}
answerOptionsToggleExpressionsMap={answerOptionsToggleExpressionsMap}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceCheckboxAnswerValueSetItem.tsx b/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceCheckboxAnswerValueSetItem.tsx
index 1d6927932..d882ed8c9 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceCheckboxAnswerValueSetItem.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceCheckboxAnswerValueSetItem.tsx
@@ -18,7 +18,7 @@
import { useMemo } from 'react';
import useAnswerOptionsToggleExpressions from '../../../hooks/useAnswerOptionsToggleExpressions';
import useReadOnly from '../../../hooks/useReadOnly';
-import useValidationFeedback from '../../../hooks/useValidationFeedback';
+import useValidationFeedbackSeverity from '../../../hooks/useValidationFeedbackSeverity';
import useValueSetCodings from '../../../hooks/useValueSetCodings';
import type { BaseItemProps } from '../../../interfaces/renderProps.interface';
import { useQuestionnaireStore } from '../../../stores';
@@ -55,7 +55,7 @@ function ChoiceCheckboxAnswerValueSetItem(props: BaseItemProps) {
const readOnly = useReadOnly(qItem, parentIsReadOnly);
// Perform validation checks
- const feedback = useValidationFeedback(qItem, feedbackFromParent);
+ const { feedback, feedbackSeverity } = useValidationFeedbackSeverity(qItem, feedbackFromParent);
const instructionsId = getInstructionsId(qItem, displayInstructions, !!feedback);
// Get codings/options from valueSet
@@ -101,6 +101,7 @@ function ChoiceCheckboxAnswerValueSetItem(props: BaseItemProps) {
options={options}
answers={answers}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
readOnly={readOnly}
expressionUpdated={calcExpUpdated || answerOptionsToggleExpUpdated}
answerOptionsToggleExpressionsMap={answerOptionsToggleExpressionsMap}
@@ -131,6 +132,7 @@ function ChoiceCheckboxAnswerValueSetItem(props: BaseItemProps) {
options={options}
answers={answers}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
readOnly={readOnly}
expressionUpdated={calcExpUpdated || answerOptionsToggleExpUpdated}
answerOptionsToggleExpressionsMap={answerOptionsToggleExpressionsMap}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceRadioAnswerOptionFields.tsx b/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceRadioAnswerOptionFields.tsx
index 60286cc62..5a779a61b 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceRadioAnswerOptionFields.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceRadioAnswerOptionFields.tsx
@@ -23,6 +23,7 @@ interface ChoiceRadioAnswerOptionFieldsProps {
options: QuestionnaireItemAnswerOption[];
valueRadio: string | null;
feedback: string;
+ feedbackSeverity?: 'error' | 'warning';
readOnly: boolean;
expressionUpdated: boolean;
answerOptionsToggleExpressionsMap: Map;
@@ -38,6 +39,7 @@ function ChoiceRadioAnswerOptionFields(props: ChoiceRadioAnswerOptionFieldsProps
options,
valueRadio,
feedback,
+ feedbackSeverity,
readOnly,
expressionUpdated,
answerOptionsToggleExpressionsMap,
@@ -53,6 +55,7 @@ function ChoiceRadioAnswerOptionFields(props: ChoiceRadioAnswerOptionFieldsProps
options={options}
valueRadio={valueRadio}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
readOnly={readOnly}
expressionUpdated={expressionUpdated}
answerOptionsToggleExpressionsMap={answerOptionsToggleExpressionsMap}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceRadioAnswerOptionItem.tsx b/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceRadioAnswerOptionItem.tsx
index b79c4e41f..560ca88bb 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceRadioAnswerOptionItem.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceRadioAnswerOptionItem.tsx
@@ -19,7 +19,7 @@ import Typography from '@mui/material/Typography';
import type { QuestionnaireItemAnswerOption } from 'fhir/r4';
import useAnswerOptionsToggleExpressions from '../../../hooks/useAnswerOptionsToggleExpressions';
import useReadOnly from '../../../hooks/useReadOnly';
-import useValidationFeedback from '../../../hooks/useValidationFeedback';
+import useValidationFeedbackSeverity from '../../../hooks/useValidationFeedbackSeverity';
import { ChoiceItemControl } from '../../../interfaces/choice.enum';
import type { BaseItemProps } from '../../../interfaces/renderProps.interface';
import { useQuestionnaireStore } from '../../../stores';
@@ -51,7 +51,7 @@ function ChoiceRadioAnswerOptionItem(props: BaseItemProps) {
const readOnly = useReadOnly(qItem, parentIsReadOnly);
// Perform validation checks - there's no string-based input here
- const feedback = useValidationFeedback(qItem, feedbackFromParent);
+ const { feedback, feedbackSeverity } = useValidationFeedbackSeverity(qItem, feedbackFromParent);
const { displayInstructions } = renderingExtensions;
@@ -108,6 +108,7 @@ function ChoiceRadioAnswerOptionItem(props: BaseItemProps) {
options={options}
valueChoice={valueChoice}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
isRepeated={isRepeated}
readOnly={readOnly}
expressionUpdated={calcExpUpdated || answerOptionsToggleExpUpdated}
@@ -128,6 +129,7 @@ function ChoiceRadioAnswerOptionItem(props: BaseItemProps) {
options={options}
valueChoice={valueChoice}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
isRepeated={isRepeated}
isTabled={isTabled}
readOnly={readOnly}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceRadioAnswerOptionView.tsx b/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceRadioAnswerOptionView.tsx
index 90d1b8669..6df1d2935 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceRadioAnswerOptionView.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceRadioAnswerOptionView.tsx
@@ -27,6 +27,7 @@ interface ChoiceRadioAnswerOptionViewProps extends PropsWithIsRepeatedAttribute
options: QuestionnaireItemAnswerOption[];
valueChoice: string | null;
feedback: string;
+ feedbackSeverity?: 'error' | 'warning';
readOnly: boolean;
expressionUpdated: boolean;
answerOptionsToggleExpressionsMap: Map;
@@ -43,6 +44,7 @@ function ChoiceRadioAnswerOptionView(props: ChoiceRadioAnswerOptionViewProps) {
options,
valueChoice,
feedback,
+ feedbackSeverity,
isRepeated,
readOnly,
expressionUpdated,
@@ -61,6 +63,7 @@ function ChoiceRadioAnswerOptionView(props: ChoiceRadioAnswerOptionViewProps) {
options={options}
valueRadio={valueChoice}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
readOnly={readOnly}
expressionUpdated={expressionUpdated}
answerOptionsToggleExpressionsMap={answerOptionsToggleExpressionsMap}
@@ -88,6 +91,7 @@ function ChoiceRadioAnswerOptionView(props: ChoiceRadioAnswerOptionViewProps) {
options={options}
valueRadio={valueChoice}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
readOnly={readOnly}
expressionUpdated={expressionUpdated}
answerOptionsToggleExpressionsMap={answerOptionsToggleExpressionsMap}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceRadioAnswerValueSetFields.tsx b/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceRadioAnswerValueSetFields.tsx
index 268cb2f64..dca379ff7 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceRadioAnswerValueSetFields.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceRadioAnswerValueSetFields.tsx
@@ -27,6 +27,7 @@ interface ChoiceRadioAnswerValueSetFieldsProps {
options: QuestionnaireItemAnswerOption[];
valueRadio: string | null;
feedback: string;
+ feedbackSeverity?: 'error' | 'warning';
readOnly: boolean;
expressionUpdated: boolean;
answerOptionsToggleExpressionsMap: Map;
@@ -43,6 +44,7 @@ function ChoiceRadioAnswerValueSetFields(props: ChoiceRadioAnswerValueSetFieldsP
options,
valueRadio,
feedback,
+ feedbackSeverity,
readOnly,
expressionUpdated,
answerOptionsToggleExpressionsMap,
@@ -60,6 +62,7 @@ function ChoiceRadioAnswerValueSetFields(props: ChoiceRadioAnswerValueSetFieldsP
options={options}
valueRadio={valueRadio}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
readOnly={readOnly}
expressionUpdated={expressionUpdated}
answerOptionsToggleExpressionsMap={answerOptionsToggleExpressionsMap}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceRadioAnswerValueSetItem.tsx b/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceRadioAnswerValueSetItem.tsx
index 566809021..2e8c1e0cd 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceRadioAnswerValueSetItem.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceRadioAnswerValueSetItem.tsx
@@ -18,7 +18,7 @@
import { useMemo } from 'react';
import useAnswerOptionsToggleExpressions from '../../../hooks/useAnswerOptionsToggleExpressions';
import useReadOnly from '../../../hooks/useReadOnly';
-import useValidationFeedback from '../../../hooks/useValidationFeedback';
+import useValidationFeedbackSeverity from '../../../hooks/useValidationFeedbackSeverity';
import useValueSetCodings from '../../../hooks/useValueSetCodings';
import type { BaseItemProps } from '../../../interfaces/renderProps.interface';
import { useQuestionnaireStore } from '../../../stores';
@@ -62,7 +62,7 @@ function ChoiceRadioAnswerValueSetItem(props: BaseItemProps) {
const readOnly = useReadOnly(qItem, parentIsReadOnly);
// Perform validation checks - there's no string-based input here
- const feedback = useValidationFeedback(qItem, feedbackFromParent);
+ const { feedback, feedbackSeverity } = useValidationFeedbackSeverity(qItem, feedbackFromParent);
const { displayInstructions } = renderingExtensions;
const instructionsId =
displayInstructions && !feedback ? `instructions-${qItem.linkId}` : undefined;
@@ -94,6 +94,7 @@ function ChoiceRadioAnswerValueSetItem(props: BaseItemProps) {
options={options}
valueRadio={valueRadio}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
readOnly={readOnly}
expressionUpdated={calcExpUpdated || dynamicCodingsUpdated || answerOptionsToggleExpUpdated}
answerOptionsToggleExpressionsMap={answerOptionsToggleExpressionsMap}
@@ -122,6 +123,7 @@ function ChoiceRadioAnswerValueSetItem(props: BaseItemProps) {
options={options}
valueRadio={valueRadio}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
readOnly={readOnly}
expressionUpdated={
calcExpUpdated || dynamicCodingsUpdated || answerOptionsToggleExpUpdated
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceSelectAnswerOptionFields.tsx b/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceSelectAnswerOptionFields.tsx
index a35b01df8..bd0a972b8 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceSelectAnswerOptionFields.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceSelectAnswerOptionFields.tsx
@@ -40,6 +40,7 @@ interface ChoiceSelectAnswerOptionFieldsProps
options: QuestionnaireItemAnswerOption[];
valueSelect: QuestionnaireItemAnswerOption | null;
feedback: string;
+ feedbackSeverity?: 'error' | 'warning';
readOnly: boolean;
expressionUpdated: boolean;
answerOptionsToggleExpressionsMap: Map;
@@ -53,6 +54,7 @@ function ChoiceSelectAnswerOptionFields(props: ChoiceSelectAnswerOptionFieldsPro
options,
valueSelect,
feedback,
+ feedbackSeverity,
readOnly,
expressionUpdated,
isTabled,
@@ -75,7 +77,7 @@ function ChoiceSelectAnswerOptionFields(props: ChoiceSelectAnswerOptionFieldsPro
return (
{feedback ? (
-
+
{feedback}
) : null}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceSelectAnswerOptionItem.tsx b/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceSelectAnswerOptionItem.tsx
index 06971e333..7a8718d54 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceSelectAnswerOptionItem.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceSelectAnswerOptionItem.tsx
@@ -19,7 +19,7 @@ import type { QuestionnaireItemAnswerOption } from 'fhir/r4';
import useAnswerOptionsToggleExpressions from '../../../hooks/useAnswerOptionsToggleExpressions';
import useReadOnly from '../../../hooks/useReadOnly';
import useRenderingExtensions from '../../../hooks/useRenderingExtensions';
-import useValidationFeedback from '../../../hooks/useValidationFeedback';
+import useValidationFeedbackSeverity from '../../../hooks/useValidationFeedbackSeverity';
import type { BaseItemProps } from '../../../interfaces/renderProps.interface';
import { useQuestionnaireStore } from '../../../stores';
import { findInAnswerOptions, getQrChoiceValue } from '../../../utils/choice';
@@ -45,7 +45,7 @@ function ChoiceSelectAnswerOptionItem(props: BaseItemProps) {
const readOnly = useReadOnly(qItem, parentIsReadOnly);
// Perform validation checks - there's no string-based input here
- const feedback = useValidationFeedback(qItem, feedbackFromParent);
+ const { feedback, feedbackSeverity } = useValidationFeedbackSeverity(qItem, feedbackFromParent);
// Get instructions ID for aria-describedby
const { displayInstructions } = useRenderingExtensions(qItem);
@@ -94,6 +94,7 @@ function ChoiceSelectAnswerOptionItem(props: BaseItemProps) {
options={options}
valueChoice={valueChoice}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
readOnly={readOnly}
expressionUpdated={calcExpUpdated || answerOptionsToggleExpUpdated}
isRepeated={isRepeated}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceSelectAnswerOptionView.tsx b/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceSelectAnswerOptionView.tsx
index b94de8065..facb05e34 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceSelectAnswerOptionView.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceSelectAnswerOptionView.tsx
@@ -36,6 +36,7 @@ interface ChoiceSelectAnswerOptionViewProps
options: QuestionnaireItemAnswerOption[];
valueChoice: string | null;
feedback: string;
+ feedbackSeverity?: 'error' | 'warning';
readOnly: boolean;
expressionUpdated: boolean;
answerOptionsToggleExpressionsMap: Map;
@@ -50,6 +51,7 @@ function ChoiceSelectAnswerOptionView(props: ChoiceSelectAnswerOptionViewProps)
options,
valueChoice,
feedback,
+ feedbackSeverity,
isRepeated,
isTabled,
renderingExtensions,
@@ -73,6 +75,7 @@ function ChoiceSelectAnswerOptionView(props: ChoiceSelectAnswerOptionViewProps)
options={options}
valueSelect={valueSelect}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
readOnly={readOnly}
expressionUpdated={expressionUpdated}
isTabled={isTabled}
@@ -100,6 +103,7 @@ function ChoiceSelectAnswerOptionView(props: ChoiceSelectAnswerOptionViewProps)
options={options}
valueSelect={valueSelect}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
readOnly={readOnly}
expressionUpdated={expressionUpdated}
isTabled={isTabled}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceSelectAnswerValueSetFields.tsx b/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceSelectAnswerValueSetFields.tsx
index 50b08cf2b..774addd26 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceSelectAnswerValueSetFields.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceSelectAnswerValueSetFields.tsx
@@ -43,6 +43,7 @@ interface ChoiceSelectAnswerValueSetFieldsProps
valueCoding: Coding | null;
terminologyError: TerminologyError;
feedback: string;
+ feedbackSeverity?: 'error' | 'warning';
readOnly: boolean;
expressionUpdated: boolean;
answerOptionsToggleExpressionsMap: Map;
@@ -57,6 +58,7 @@ function ChoiceSelectAnswerValueSetFields(props: ChoiceSelectAnswerValueSetField
valueCoding,
terminologyError,
feedback,
+ feedbackSeverity,
readOnly,
expressionUpdated,
isTabled,
@@ -84,7 +86,7 @@ function ChoiceSelectAnswerValueSetFields(props: ChoiceSelectAnswerValueSetField
if (codings.length > 0) {
return (
{feedback ? (
-
+
{feedback}
) : null}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceSelectAnswerValueSetItem.tsx b/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceSelectAnswerValueSetItem.tsx
index 077c8be39..31474a5f3 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceSelectAnswerValueSetItem.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/ChoiceItems/ChoiceSelectAnswerValueSetItem.tsx
@@ -20,7 +20,7 @@ import { useEffect, useMemo } from 'react';
import type { Coding } from 'fhir/r4';
import useAnswerOptionsToggleExpressions from '../../../hooks/useAnswerOptionsToggleExpressions';
import useReadOnly from '../../../hooks/useReadOnly';
-import useValidationFeedback from '../../../hooks/useValidationFeedback';
+import useValidationFeedbackSeverity from '../../../hooks/useValidationFeedbackSeverity';
import useValueSetCodings from '../../../hooks/useValueSetCodings';
import type { BaseItemProps } from '../../../interfaces/renderProps.interface';
import { useQuestionnaireStore } from '../../../stores';
@@ -57,7 +57,7 @@ function ChoiceSelectAnswerValueSetItem(props: BaseItemProps) {
const readOnly = useReadOnly(qItem, parentIsReadOnly);
// Perform validation checks
- const feedback = useValidationFeedback(qItem, feedbackFromParent);
+ const { feedback, feedbackSeverity } = useValidationFeedbackSeverity(qItem, feedbackFromParent);
const { displayInstructions } = renderingExtensions;
const instructionsId =
displayInstructions && !feedback ? `instructions-${qItem.linkId}` : undefined;
@@ -110,6 +110,7 @@ function ChoiceSelectAnswerValueSetItem(props: BaseItemProps) {
valueCoding={valueCoding}
terminologyError={terminologyError}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
readOnly={readOnly}
expressionUpdated={calcExpUpdated || dynamicCodingsUpdated || answerOptionsToggleExpUpdated}
isTabled={isTabled}
@@ -136,6 +137,7 @@ function ChoiceSelectAnswerValueSetItem(props: BaseItemProps) {
valueCoding={valueCoding}
terminologyError={terminologyError}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
readOnly={readOnly}
expressionUpdated={calcExpUpdated || dynamicCodingsUpdated}
isTabled={isTabled}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/DateTimeItems/CustomDateItem/CustomDateField.tsx b/packages/smart-forms-renderer/src/components/FormComponents/DateTimeItems/CustomDateItem/CustomDateField.tsx
index 3cb87ff73..9bd5fcbd3 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/DateTimeItems/CustomDateItem/CustomDateField.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/DateTimeItems/CustomDateItem/CustomDateField.tsx
@@ -32,6 +32,7 @@ interface CustomDateFieldProps extends PropsWithIsTabledAttribute {
valueDate: string;
input: string;
feedback: string;
+ feedbackSeverity?: 'error' | 'warning';
isFocused: boolean;
displayPrompt: string;
entryFormat: string;
@@ -52,6 +53,7 @@ function CustomDateField(props: CustomDateFieldProps) {
valueDate,
input,
feedback,
+ feedbackSeverity,
isFocused,
displayPrompt,
entryFormat,
@@ -93,7 +95,7 @@ function CustomDateField(props: CustomDateFieldProps) {
textFieldWidth={textFieldWidth}
isTabled={isTabled}
value={input}
- error={!!feedback}
+ error={!!feedback && feedbackSeverity !== 'warning'}
onChange={(e: ChangeEvent) => onInputChange(e.target.value)}
placeholder={placeholderText}
disabled={readOnly && readOnlyVisualStyle === 'disabled'}
@@ -122,6 +124,9 @@ function CustomDateField(props: CustomDateFieldProps) {
htmlInput: {
...(isTabled ? {} : { 'aria-label': itemText ?? `Unnamed ${itemType} item` }),
...(instructionsId && { 'aria-describedby': instructionsId })
+ },
+ formHelperText: {
+ sx: feedbackSeverity === 'warning' && !!feedback ? { color: 'warning.main' } : undefined
}
}}
helperText={feedback}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/DateTimeItems/CustomDateItem/CustomDateItem.tsx b/packages/smart-forms-renderer/src/components/FormComponents/DateTimeItems/CustomDateItem/CustomDateItem.tsx
index 4cba130a6..b06ab968c 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/DateTimeItems/CustomDateItem/CustomDateItem.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/DateTimeItems/CustomDateItem/CustomDateItem.tsx
@@ -18,7 +18,7 @@
import { useState } from 'react';
import useDateValidation from '../../../../hooks/useDateValidation';
import useReadOnly from '../../../../hooks/useReadOnly';
-import useValidationFeedback from '../../../../hooks/useValidationFeedback';
+import useValidationFeedbackSeverity from '../../../../hooks/useValidationFeedbackSeverity';
import type { BaseItemProps } from '../../../../interfaces/renderProps.interface';
import { useQuestionnaireStore } from '../../../../stores';
import { createEmptyQrItem, getQRItemId } from '../../../../utils/qrItem';
@@ -68,7 +68,10 @@ function CustomDateItem(props: BaseItemProps) {
// Perform validation checks
// Constraint and required feedback takes priority; fall back to date format feedback.
- const validationFeedback = useValidationFeedback(qItem, undefined);
+ const { feedback: validationFeedback, feedbackSeverity } = useValidationFeedbackSeverity(
+ qItem,
+ undefined
+ );
const dateValidationFeedback = useDateValidation(input, dateParseFail);
const feedback = validationFeedback || dateValidationFeedback;
@@ -109,6 +112,7 @@ function CustomDateItem(props: BaseItemProps) {
valueDate={displayDate}
input={input}
feedback={feedback ?? ''}
+ feedbackSeverity={feedbackSeverity}
isFocused={focused}
displayPrompt={displayPrompt}
entryFormat={entryFormat}
@@ -142,6 +146,7 @@ function CustomDateItem(props: BaseItemProps) {
valueDate={displayDate}
input={input}
feedback={feedback ?? ''}
+ feedbackSeverity={feedbackSeverity}
isFocused={focused}
displayPrompt={displayPrompt}
entryFormat={entryFormat}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/DateTimeItems/CustomDateTimeItem/CustomDateTimeItem.tsx b/packages/smart-forms-renderer/src/components/FormComponents/DateTimeItems/CustomDateTimeItem/CustomDateTimeItem.tsx
index e61207680..2b6892c2f 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/DateTimeItems/CustomDateTimeItem/CustomDateTimeItem.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/DateTimeItems/CustomDateTimeItem/CustomDateTimeItem.tsx
@@ -23,7 +23,7 @@ import useDateNonEmptyValidation from '../../../../hooks/useDateTimeNonEmpty';
import useDateValidation from '../../../../hooks/useDateValidation';
import useReadOnly from '../../../../hooks/useReadOnly';
import useTimeValidation from '../../../../hooks/useTimeValidation';
-import useValidationFeedback from '../../../../hooks/useValidationFeedback';
+import useValidationFeedbackSeverity from '../../../../hooks/useValidationFeedbackSeverity';
import type { BaseItemProps } from '../../../../interfaces/renderProps.interface';
import { useQuestionnaireStore } from '../../../../stores';
import { createEmptyQrItem, getQRItemId } from '../../../../utils/qrItem';
@@ -97,7 +97,10 @@ function CustomDateTimeItem(props: BaseItemProps) {
// Perform validation checks
// Constraint and required feedback takes priority; fall back to date/time format feedback.
- const validationFeedback = useValidationFeedback(qItem, undefined);
+ const { feedback: validationFeedback, feedbackSeverity } = useValidationFeedbackSeverity(
+ qItem,
+ undefined
+ );
let dateFeedback = useDateValidation(dateInput, dateParseFail);
const { timeFeedback, is24HourNotation } = useTimeValidation(
timeInput,
@@ -192,6 +195,7 @@ function CustomDateTimeItem(props: BaseItemProps) {
periodInput={periodInput}
is24HourNotation={is24HourNotation}
dateFeedback={dateFeedback ?? ''}
+ dateFeedbackSeverity={feedbackSeverity}
timeFeedback={timeFeedback ?? ''}
dateFocused={dateFocused}
displayPrompt={displayPrompt}
@@ -230,6 +234,7 @@ function CustomDateTimeItem(props: BaseItemProps) {
periodInput={periodInput}
is24HourNotation={is24HourNotation}
dateFeedback={dateFeedback ?? ''}
+ dateFeedbackSeverity={feedbackSeverity}
timeFeedback={timeFeedback ?? ''}
dateFocused={dateFocused}
displayPrompt={displayPrompt}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/DateTimeItems/CustomDateTimeItem/DateTimeField.tsx b/packages/smart-forms-renderer/src/components/FormComponents/DateTimeItems/CustomDateTimeItem/DateTimeField.tsx
index 5df244658..fbd380271 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/DateTimeItems/CustomDateTimeItem/DateTimeField.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/DateTimeItems/CustomDateTimeItem/DateTimeField.tsx
@@ -31,6 +31,7 @@ interface DateTimeFieldProps extends PropsWithIsTabledAttribute {
periodInput: string;
is24HourNotation: boolean;
dateFeedback: string;
+ dateFeedbackSeverity?: 'error' | 'warning';
timeFeedback: string;
dateFocused: boolean;
displayPrompt: string;
@@ -55,6 +56,7 @@ function DateTimeField(props: DateTimeFieldProps) {
periodInput,
is24HourNotation,
dateFeedback,
+ dateFeedbackSeverity,
timeFeedback,
dateFocused,
displayPrompt,
@@ -78,6 +80,7 @@ function DateTimeField(props: DateTimeFieldProps) {
valueDate={displayDate}
input={dateInput}
feedback={dateFeedback ?? ''}
+ feedbackSeverity={dateFeedbackSeverity}
isFocused={dateFocused}
displayPrompt={displayPrompt}
entryFormat={entryFormat}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/DecimalItem/DecimalField.tsx b/packages/smart-forms-renderer/src/components/FormComponents/DecimalItem/DecimalField.tsx
index f5dc46a6f..f8af464f0 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/DecimalItem/DecimalField.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/DecimalItem/DecimalField.tsx
@@ -30,6 +30,7 @@ interface DecimalFieldProps extends PropsWithIsTabledAttribute {
qItem: QuestionnaireItem;
input: string;
feedback: string;
+ feedbackSeverity?: 'error' | 'warning';
renderingExtensions: RenderingExtensions;
readOnly: boolean;
calcExpUpdated: boolean;
@@ -43,6 +44,7 @@ function DecimalField(props: DecimalFieldProps) {
qItem,
input,
feedback,
+ feedbackSeverity,
renderingExtensions,
readOnly,
calcExpUpdated,
@@ -75,7 +77,7 @@ function DecimalField(props: DecimalFieldProps) {
onInputChange(event.target.value)}
disabled={readOnly && readOnlyVisualStyle === 'disabled'}
@@ -91,6 +93,9 @@ function DecimalField(props: DecimalFieldProps) {
...(ariaLabel && { 'aria-label': ariaLabel }),
...(instructionsId && { 'aria-describedby': instructionsId })
},
+ formHelperText: {
+ sx: feedbackSeverity === 'warning' && !!feedback ? { color: 'warning.main' } : undefined
+ },
input: {
readOnly: readOnly && readOnlyVisualStyle === 'readonly',
endAdornment: (
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/DecimalItem/DecimalItem.tsx b/packages/smart-forms-renderer/src/components/FormComponents/DecimalItem/DecimalItem.tsx
index 3bf939ca3..3c9f6ebd8 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/DecimalItem/DecimalItem.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/DecimalItem/DecimalItem.tsx
@@ -18,7 +18,7 @@
import debounce from 'lodash.debounce';
import { useCallback, useState } from 'react';
import useReadOnly from '../../../hooks/useReadOnly';
-import useValidationFeedback from '../../../hooks/useValidationFeedback';
+import useValidationFeedbackSeverity from '../../../hooks/useValidationFeedbackSeverity';
import useRenderingExtensions from '../../../hooks/useRenderingExtensions';
import type { BaseItemProps } from '../../../interfaces/renderProps.interface';
import { useQuestionnaireStore } from '../../../stores';
@@ -62,7 +62,7 @@ function DecimalItem(props: BaseItemProps) {
const readOnly = useReadOnly(qItem, parentIsReadOnly);
// Perform validation checks - there's no string-based input here
- const feedback = useValidationFeedback(qItem, feedbackFromParent);
+ const { feedback, feedbackSeverity } = useValidationFeedbackSeverity(qItem, feedbackFromParent);
const { displayInstructions } = useRenderingExtensions(qItem);
@@ -127,6 +127,7 @@ function DecimalItem(props: BaseItemProps) {
qItem={qItem}
input={input}
feedback={feedback ?? ''}
+ feedbackSeverity={feedbackSeverity}
renderingExtensions={renderingExtensions}
readOnly={readOnly}
calcExpUpdated={calcExpUpdated}
@@ -153,6 +154,7 @@ function DecimalItem(props: BaseItemProps) {
qItem={qItem}
input={input}
feedback={feedback ?? ''}
+ feedbackSeverity={feedbackSeverity}
renderingExtensions={renderingExtensions}
readOnly={readOnly}
calcExpUpdated={calcExpUpdated}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/IntegerItem/IntegerField.tsx b/packages/smart-forms-renderer/src/components/FormComponents/IntegerItem/IntegerField.tsx
index c87b083e9..7afc075fd 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/IntegerItem/IntegerField.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/IntegerItem/IntegerField.tsx
@@ -31,6 +31,7 @@ interface IntegerFieldProps extends PropsWithIsTabledAttribute {
qItem: QuestionnaireItem;
input: string;
feedback: string;
+ feedbackSeverity?: 'error' | 'warning';
renderingExtensions: RenderingExtensions;
readOnly: boolean;
calcExpUpdated: boolean;
@@ -44,6 +45,7 @@ function IntegerField(props: IntegerFieldProps) {
qItem,
input,
feedback,
+ feedbackSeverity,
renderingExtensions,
readOnly,
calcExpUpdated,
@@ -76,7 +78,7 @@ function IntegerField(props: IntegerFieldProps) {
{feedback}}
onChange={(event) => onInputChange(event.target.value)}
disabled={readOnly && readOnlyVisualStyle === 'disabled'}
@@ -93,6 +95,9 @@ function IntegerField(props: IntegerFieldProps) {
...(ariaLabel && { 'aria-label': ariaLabel }),
...(instructionsId && { 'aria-describedby': instructionsId })
},
+ formHelperText: {
+ sx: feedbackSeverity === 'warning' && !!feedback ? { color: 'warning.main' } : undefined
+ },
input: {
readOnly: readOnly && readOnlyVisualStyle === 'readonly',
endAdornment: (
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/IntegerItem/IntegerItem.tsx b/packages/smart-forms-renderer/src/components/FormComponents/IntegerItem/IntegerItem.tsx
index 92a07e265..209287f23 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/IntegerItem/IntegerItem.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/IntegerItem/IntegerItem.tsx
@@ -18,7 +18,7 @@
import debounce from 'lodash.debounce';
import { useCallback, useState } from 'react';
import useReadOnly from '../../../hooks/useReadOnly';
-import useValidationFeedback from '../../../hooks/useValidationFeedback';
+import useValidationFeedbackSeverity from '../../../hooks/useValidationFeedbackSeverity';
import useRenderingExtensions from '../../../hooks/useRenderingExtensions';
import type { BaseItemProps } from '../../../interfaces/renderProps.interface';
import { useQuestionnaireStore } from '../../../stores';
@@ -56,7 +56,7 @@ function IntegerItem(props: BaseItemProps) {
const readOnly = useReadOnly(qItem, parentIsReadOnly);
// Perform validation checks
- const feedback = useValidationFeedback(qItem, feedbackFromParent);
+ const { feedback, feedbackSeverity } = useValidationFeedbackSeverity(qItem, feedbackFromParent);
const { displayInstructions } = useRenderingExtensions(qItem);
@@ -111,6 +111,7 @@ function IntegerItem(props: BaseItemProps) {
qItem={qItem}
input={input}
feedback={feedback ?? ''}
+ feedbackSeverity={feedbackSeverity}
renderingExtensions={renderingExtensions}
readOnly={readOnly}
calcExpUpdated={calcExpUpdated}
@@ -137,6 +138,7 @@ function IntegerItem(props: BaseItemProps) {
qItem={qItem}
input={input}
feedback={feedback ?? ''}
+ feedbackSeverity={feedbackSeverity}
renderingExtensions={renderingExtensions}
readOnly={readOnly}
calcExpUpdated={calcExpUpdated}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/ItemParts/CheckboxFormGroup.tsx b/packages/smart-forms-renderer/src/components/FormComponents/ItemParts/CheckboxFormGroup.tsx
index cb11162d9..27ab81b6e 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/ItemParts/CheckboxFormGroup.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/ItemParts/CheckboxFormGroup.tsx
@@ -19,6 +19,7 @@ interface ChoiceCheckboxFormGroupProps {
options: QuestionnaireItemAnswerOption[];
answers: QuestionnaireResponseItemAnswer[];
feedback: string;
+ feedbackSeverity?: 'error' | 'warning';
readOnly: boolean;
expressionUpdated: boolean;
answerOptionsToggleExpressionsMap: Map;
@@ -35,6 +36,7 @@ function CheckboxFormGroup(props: ChoiceCheckboxFormGroupProps) {
options,
answers,
feedback,
+ feedbackSeverity,
readOnly,
expressionUpdated,
answerOptionsToggleExpressionsMap,
@@ -94,7 +96,12 @@ function CheckboxFormGroup(props: ChoiceCheckboxFormGroupProps) {
- {feedback ? {feedback} : null}
+ {feedback ? (
+
+ {feedback}
+
+ ) : null}
>
);
}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/ItemParts/RadioFormGroup.tsx b/packages/smart-forms-renderer/src/components/FormComponents/ItemParts/RadioFormGroup.tsx
index 5b0d703a5..3b7ec1e85 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/ItemParts/RadioFormGroup.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/ItemParts/RadioFormGroup.tsx
@@ -15,6 +15,7 @@ interface ChoiceRadioGroupProps {
options: QuestionnaireItemAnswerOption[];
valueRadio: string | null;
feedback: string;
+ feedbackSeverity?: 'error' | 'warning';
readOnly: boolean;
expressionUpdated: boolean;
answerOptionsToggleExpressionsMap: Map;
@@ -31,6 +32,7 @@ function RadioFormGroup(props: ChoiceRadioGroupProps) {
options,
valueRadio,
feedback,
+ feedbackSeverity,
readOnly,
expressionUpdated,
answerOptionsToggleExpressionsMap,
@@ -99,7 +101,12 @@ function RadioFormGroup(props: ChoiceRadioGroupProps) {
- {feedback ? {feedback} : null}
+ {feedback ? (
+
+ {feedback}
+
+ ) : null}
>
);
}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceAutocompleteItem.tsx b/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceAutocompleteItem.tsx
index 319341c10..d369a05c6 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceAutocompleteItem.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceAutocompleteItem.tsx
@@ -31,7 +31,7 @@ import OpenChoiceAutocompleteField from './OpenChoiceAutocompleteField';
import ItemFieldGrid, { getInstructionsId } from '../ItemParts/ItemFieldGrid';
import ItemLabel from '../ItemParts/ItemLabel';
import { sanitizeInput } from '../../../utils/inputSanitization';
-import { useValidationFeedback } from '../../../hooks';
+import useValidationFeedbackSeverity from '../../../hooks/useValidationFeedbackSeverity';
import { createEmptyQrItem } from '../../../utils';
import type { BaseItemProps } from '../../../interfaces/renderProps.interface';
import { useQuestionnaireStore } from '../../../stores';
@@ -75,7 +75,8 @@ function OpenChoiceAutocompleteItem(props: BaseItemProps) {
const readOnly = useReadOnly(qItem, parentIsReadOnly);
// Perform validation checks
- const validationFeedback = useValidationFeedback(qItem, feedbackFromParent);
+ const { feedback: validationFeedback, feedbackSeverity: validationFeedbackSeverity } =
+ useValidationFeedbackSeverity(qItem, feedbackFromParent);
const { displayInstructions } = useRenderingExtensions(qItem);
const instructionsId = getInstructionsId(qItem, displayInstructions, !!validationFeedback);
@@ -89,7 +90,7 @@ function OpenChoiceAutocompleteItem(props: BaseItemProps) {
if (terminologyFeedback) {
feedback = terminologyFeedback;
} else if (validationFeedback !== '') {
- feedback = { message: validationFeedback, color: 'error' };
+ feedback = { message: validationFeedback, color: validationFeedbackSeverity };
}
// eslint-disable-next-line react-hooks/exhaustive-deps
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceCheckboxAnswerOptionFields.tsx b/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceCheckboxAnswerOptionFields.tsx
index aeb04715c..100441bea 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceCheckboxAnswerOptionFields.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceCheckboxAnswerOptionFields.tsx
@@ -31,6 +31,7 @@ interface OpenChoiceCheckboxAnswerOptionFieldsProps {
openLabelValue: string;
openLabelChecked: boolean;
feedback: string;
+ feedbackSeverity?: 'error' | 'warning';
readOnly: boolean;
expressionUpdated: boolean;
answerOptionsToggleExpressionsMap: Map;
@@ -51,6 +52,7 @@ function OpenChoiceCheckboxAnswerOptionFields(props: OpenChoiceCheckboxAnswerOpt
openLabelValue,
openLabelChecked,
feedback,
+ feedbackSeverity,
readOnly,
expressionUpdated,
answerOptionsToggleExpressionsMap,
@@ -68,6 +70,7 @@ function OpenChoiceCheckboxAnswerOptionFields(props: OpenChoiceCheckboxAnswerOpt
options={options}
answers={answers}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
readOnly={readOnly}
expressionUpdated={expressionUpdated}
answerOptionsToggleExpressionsMap={answerOptionsToggleExpressionsMap}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceCheckboxAnswerOptionItem.tsx b/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceCheckboxAnswerOptionItem.tsx
index 050d56497..a75441260 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceCheckboxAnswerOptionItem.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceCheckboxAnswerOptionItem.tsx
@@ -20,7 +20,7 @@ import { useCallback } from 'react';
import useAnswerOptionsToggleExpressions from '../../../hooks/useAnswerOptionsToggleExpressions';
import useOpenLabel from '../../../hooks/useOpenLabel';
import useReadOnly from '../../../hooks/useReadOnly';
-import useValidationFeedback from '../../../hooks/useValidationFeedback';
+import useValidationFeedbackSeverity from '../../../hooks/useValidationFeedbackSeverity';
import type { BaseItemProps } from '../../../interfaces/renderProps.interface';
import { useQuestionnaireStore } from '../../../stores';
import { updateChoiceCheckboxAnswers } from '../../../utils/choice';
@@ -57,7 +57,7 @@ function OpenChoiceCheckboxAnswerOptionItem(props: BaseItemProps) {
const readOnly = useReadOnly(qItem, parentIsReadOnly);
// Perform validation checks
- const feedback = useValidationFeedback(qItem, feedbackFromParent);
+ const { feedback, feedbackSeverity } = useValidationFeedbackSeverity(qItem, feedbackFromParent);
const { displayInstructions } = renderingExtensions;
const instructionsId = getInstructionsId(qItem, displayInstructions, !!feedback);
const openLabelText = getOpenLabelText(qItem);
@@ -160,6 +160,7 @@ function OpenChoiceCheckboxAnswerOptionItem(props: BaseItemProps) {
openLabelValue={openLabelValue}
openLabelChecked={openLabelChecked}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
readOnly={readOnly}
expressionUpdated={calcExpUpdated || answerOptionsToggleExpUpdated}
answerOptionsToggleExpressionsMap={answerOptionsToggleExpressionsMap}
@@ -194,6 +195,7 @@ function OpenChoiceCheckboxAnswerOptionItem(props: BaseItemProps) {
openLabelValue={openLabelValue}
openLabelChecked={openLabelChecked}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
readOnly={readOnly}
expressionUpdated={calcExpUpdated || answerOptionsToggleExpUpdated}
answerOptionsToggleExpressionsMap={answerOptionsToggleExpressionsMap}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceCheckboxAnswerValueSetFields.tsx b/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceCheckboxAnswerValueSetFields.tsx
index 4f3c62d60..45fdaebab 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceCheckboxAnswerValueSetFields.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceCheckboxAnswerValueSetFields.tsx
@@ -35,6 +35,7 @@ interface OpenChoiceCheckboxFieldsProps {
openLabelValue: string;
openLabelChecked: boolean;
feedback: string;
+ feedbackSeverity?: 'error' | 'warning';
readOnly: boolean;
expressionUpdated: boolean;
answerOptionsToggleExpressionsMap: Map;
@@ -56,6 +57,7 @@ function OpenChoiceCheckboxAnswerValueSetFields(props: OpenChoiceCheckboxFieldsP
openLabelValue,
openLabelChecked,
feedback,
+ feedbackSeverity,
readOnly,
expressionUpdated,
answerOptionsToggleExpressionsMap,
@@ -75,6 +77,7 @@ function OpenChoiceCheckboxAnswerValueSetFields(props: OpenChoiceCheckboxFieldsP
options={options}
answers={answers}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
readOnly={readOnly}
expressionUpdated={expressionUpdated}
answerOptionsToggleExpressionsMap={answerOptionsToggleExpressionsMap}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceCheckboxAnswerValueSetItem.tsx b/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceCheckboxAnswerValueSetItem.tsx
index 9e3281f75..4241a75ad 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceCheckboxAnswerValueSetItem.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceCheckboxAnswerValueSetItem.tsx
@@ -20,7 +20,7 @@ import { useCallback, useMemo } from 'react';
import useAnswerOptionsToggleExpressions from '../../../hooks/useAnswerOptionsToggleExpressions';
import useOpenLabel from '../../../hooks/useOpenLabel';
import useReadOnly from '../../../hooks/useReadOnly';
-import useValidationFeedback from '../../../hooks/useValidationFeedback';
+import useValidationFeedbackSeverity from '../../../hooks/useValidationFeedbackSeverity';
import useValueSetCodings from '../../../hooks/useValueSetCodings';
import type { BaseItemProps } from '../../../interfaces/renderProps.interface';
import { useQuestionnaireStore } from '../../../stores';
@@ -58,7 +58,7 @@ function OpenChoiceCheckboxAnswerValueSetItem(props: BaseItemProps) {
const readOnly = useReadOnly(qItem, parentIsReadOnly);
// Perform validation checks
- const feedback = useValidationFeedback(qItem, feedbackFromParent);
+ const { feedback, feedbackSeverity } = useValidationFeedbackSeverity(qItem, feedbackFromParent);
const { displayInstructions } = renderingExtensions;
const instructionsId = getInstructionsId(qItem, displayInstructions, !!feedback);
const openLabelText = getOpenLabelText(qItem);
@@ -164,6 +164,7 @@ function OpenChoiceCheckboxAnswerValueSetItem(props: BaseItemProps) {
openLabelValue={openLabelValue}
openLabelChecked={openLabelChecked}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
readOnly={readOnly}
expressionUpdated={
calcExpUpdated || dynamicCodingsUpdated || answerOptionsToggleExpUpdated
@@ -201,6 +202,7 @@ function OpenChoiceCheckboxAnswerValueSetItem(props: BaseItemProps) {
openLabelValue={openLabelValue}
openLabelChecked={openLabelChecked}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
readOnly={readOnly}
expressionUpdated={
calcExpUpdated || dynamicCodingsUpdated || answerOptionsToggleExpUpdated
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceRadioAnswerOptionFields.tsx b/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceRadioAnswerOptionFields.tsx
index b6870eaf4..7560b01b9 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceRadioAnswerOptionFields.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceRadioAnswerOptionFields.tsx
@@ -27,6 +27,7 @@ interface OpenChoiceRadioAnswerOptionFieldsProps {
openLabelValue: string | null;
openLabelSelected: boolean;
feedback: string;
+ feedbackSeverity?: 'error' | 'warning';
readOnly: boolean;
expressionUpdated: boolean;
answerOptionsToggleExpressionsMap: Map;
@@ -45,6 +46,7 @@ function OpenChoiceRadioAnswerOptionFields(props: OpenChoiceRadioAnswerOptionFie
openLabelValue,
openLabelSelected,
feedback,
+ feedbackSeverity,
readOnly,
expressionUpdated,
answerOptionsToggleExpressionsMap,
@@ -61,6 +63,7 @@ function OpenChoiceRadioAnswerOptionFields(props: OpenChoiceRadioAnswerOptionFie
options={options}
valueRadio={valueRadio}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
readOnly={readOnly}
expressionUpdated={expressionUpdated}
answerOptionsToggleExpressionsMap={answerOptionsToggleExpressionsMap}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceRadioAnswerOptionItem.tsx b/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceRadioAnswerOptionItem.tsx
index 4a65cc1b8..edd5992e1 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceRadioAnswerOptionItem.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceRadioAnswerOptionItem.tsx
@@ -19,7 +19,7 @@ import { useState } from 'react';
import useAnswerOptionsToggleExpressions from '../../../hooks/useAnswerOptionsToggleExpressions';
import useReadOnly from '../../../hooks/useReadOnly';
import useRenderingExtensions from '../../../hooks/useRenderingExtensions';
-import useValidationFeedback from '../../../hooks/useValidationFeedback';
+import useValidationFeedbackSeverity from '../../../hooks/useValidationFeedbackSeverity';
import type { BaseItemProps } from '../../../interfaces/renderProps.interface';
import { useQuestionnaireStore } from '../../../stores';
import { findInAnswerOptions, getQrChoiceValue } from '../../../utils/choice';
@@ -54,7 +54,7 @@ function OpenChoiceRadioAnswerOptionItem(props: BaseItemProps) {
const readOnly = useReadOnly(qItem, parentIsReadOnly);
// Perform validation checks
- const feedback = useValidationFeedback(qItem, feedbackFromParent);
+ const { feedback, feedbackSeverity } = useValidationFeedbackSeverity(qItem, feedbackFromParent);
const { displayInstructions } = useRenderingExtensions(qItem);
const instructionsId = getInstructionsId(qItem, displayInstructions, !!feedback);
@@ -168,6 +168,7 @@ function OpenChoiceRadioAnswerOptionItem(props: BaseItemProps) {
openLabelValue={openLabelValue}
openLabelSelected={openLabelSelected}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
readOnly={readOnly}
expressionUpdated={calcExpUpdated || answerOptionsToggleExpUpdated}
answerOptionsToggleExpressionsMap={answerOptionsToggleExpressionsMap}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceRadioAnswerValueSetFields.tsx b/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceRadioAnswerValueSetFields.tsx
index 28347dd2d..03073de5e 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceRadioAnswerValueSetFields.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceRadioAnswerValueSetFields.tsx
@@ -31,6 +31,7 @@ interface OpenChoiceRadioAnswerValueSetFieldsProps {
openLabelValue: string | null;
openLabelSelected: boolean;
feedback: string;
+ feedbackSeverity?: 'error' | 'warning';
readOnly: boolean;
expressionUpdated: boolean;
answerOptionsToggleExpressionsMap: Map;
@@ -50,6 +51,7 @@ function OpenChoiceRadioAnswerValueSetFields(props: OpenChoiceRadioAnswerValueSe
openLabelValue,
openLabelSelected,
feedback,
+ feedbackSeverity,
readOnly,
expressionUpdated,
answerOptionsToggleExpressionsMap,
@@ -67,6 +69,7 @@ function OpenChoiceRadioAnswerValueSetFields(props: OpenChoiceRadioAnswerValueSe
options={options}
valueRadio={valueRadio}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
readOnly={readOnly}
expressionUpdated={expressionUpdated}
answerOptionsToggleExpressionsMap={answerOptionsToggleExpressionsMap}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceRadioAnswerValueSetItem.tsx b/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceRadioAnswerValueSetItem.tsx
index 60a82c0bb..15f22fe6f 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceRadioAnswerValueSetItem.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceRadioAnswerValueSetItem.tsx
@@ -19,7 +19,7 @@ import { useMemo, useState } from 'react';
import useAnswerOptionsToggleExpressions from '../../../hooks/useAnswerOptionsToggleExpressions';
import useReadOnly from '../../../hooks/useReadOnly';
import useRenderingExtensions from '../../../hooks/useRenderingExtensions';
-import useValidationFeedback from '../../../hooks/useValidationFeedback';
+import useValidationFeedbackSeverity from '../../../hooks/useValidationFeedbackSeverity';
import useValueSetCodings from '../../../hooks/useValueSetCodings';
import type { BaseItemProps } from '../../../interfaces/renderProps.interface';
import { useQuestionnaireStore } from '../../../stores';
@@ -59,7 +59,7 @@ function OpenChoiceRadioAnswerValueSetItem(props: BaseItemProps) {
const readOnly = useReadOnly(qItem, parentIsReadOnly);
// Perform validation checks
- const feedback = useValidationFeedback(qItem, feedbackFromParent);
+ const { feedback, feedbackSeverity } = useValidationFeedbackSeverity(qItem, feedbackFromParent);
const { displayInstructions } = useRenderingExtensions(qItem);
const instructionsId = getInstructionsId(qItem, displayInstructions, !!feedback);
@@ -176,6 +176,7 @@ function OpenChoiceRadioAnswerValueSetItem(props: BaseItemProps) {
openLabelValue={openLabelValue}
openLabelSelected={openLabelSelected}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
readOnly={readOnly}
expressionUpdated={
calcExpUpdated || dynamicCodingsUpdated || answerOptionsToggleExpUpdated
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceSelectAnswerOptionField.tsx b/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceSelectAnswerOptionField.tsx
index 3c7c2ee3b..a235a2aec 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceSelectAnswerOptionField.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceSelectAnswerOptionField.tsx
@@ -42,6 +42,7 @@ interface OpenChoiceSelectAnswerOptionFieldProps
options: QuestionnaireItemAnswerOption[];
valueSelect: QuestionnaireItemAnswerOption | null;
feedback: string;
+ feedbackSeverity?: 'error' | 'warning';
readOnly: boolean;
calcExpUpdated: boolean;
instructionsId?: string;
@@ -57,6 +58,7 @@ function OpenChoiceSelectAnswerOptionField(props: OpenChoiceSelectAnswerOptionFi
options,
valueSelect,
feedback,
+ feedbackSeverity,
readOnly,
calcExpUpdated,
instructionsId,
@@ -74,7 +76,7 @@ function OpenChoiceSelectAnswerOptionField(props: OpenChoiceSelectAnswerOptionFi
return (
{feedback ? (
-
+
{feedback}
) : null}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceSelectAnswerOptionItem.tsx b/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceSelectAnswerOptionItem.tsx
index 714e17709..ffdeba266 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceSelectAnswerOptionItem.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceSelectAnswerOptionItem.tsx
@@ -19,7 +19,7 @@ import type { AutocompleteChangeReason } from '@mui/material';
import type { QuestionnaireItemAnswerOption } from 'fhir/r4';
import useReadOnly from '../../../hooks/useReadOnly';
import useRenderingExtensions from '../../../hooks/useRenderingExtensions';
-import useValidationFeedback from '../../../hooks/useValidationFeedback';
+import useValidationFeedbackSeverity from '../../../hooks/useValidationFeedbackSeverity';
import type { BaseItemProps } from '../../../interfaces/renderProps.interface';
import { useQuestionnaireStore } from '../../../stores';
import { createEmptyQrItem, getQRItemId } from '../../../utils/qrItem';
@@ -47,7 +47,7 @@ function OpenChoiceSelectAnswerOptionItem(props: BaseItemProps) {
const readOnly = useReadOnly(qItem, parentIsReadOnly);
// Perform validation checks
- const feedback = useValidationFeedback(qItem, feedbackFromParent);
+ const { feedback, feedbackSeverity } = useValidationFeedbackSeverity(qItem, feedbackFromParent);
const { displayInstructions } = useRenderingExtensions(qItem);
const instructionsId = getInstructionsId(qItem, displayInstructions, !!feedback);
@@ -112,6 +112,7 @@ function OpenChoiceSelectAnswerOptionItem(props: BaseItemProps) {
options={answerOptions}
valueSelect={valueSelect}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
readOnly={readOnly}
calcExpUpdated={calcExpUpdated}
isTabled={isTabled}
@@ -138,6 +139,7 @@ function OpenChoiceSelectAnswerOptionItem(props: BaseItemProps) {
options={answerOptions}
valueSelect={valueSelect}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
readOnly={readOnly}
calcExpUpdated={calcExpUpdated}
isTabled={isTabled}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceSelectAnswerValueSetField.tsx b/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceSelectAnswerValueSetField.tsx
index 4c93f7a7e..68903b62c 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceSelectAnswerValueSetField.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceSelectAnswerValueSetField.tsx
@@ -43,6 +43,7 @@ interface OpenChoiceSelectAnswerValueSetFieldProps
valueSelect: Coding | string | null;
terminologyError: TerminologyError;
feedback: string;
+ feedbackSeverity?: 'error' | 'warning';
readOnly: boolean;
calcExpUpdated: boolean;
instructionsId?: string;
@@ -59,6 +60,7 @@ function OpenChoiceSelectAnswerValueSetField(props: OpenChoiceSelectAnswerValueS
valueSelect,
terminologyError,
feedback,
+ feedbackSeverity,
readOnly,
calcExpUpdated,
instructionsId,
@@ -74,7 +76,7 @@ function OpenChoiceSelectAnswerValueSetField(props: OpenChoiceSelectAnswerValueS
return (
+
{feedback}
) : null}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceSelectAnswerValueSetItem.tsx b/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceSelectAnswerValueSetItem.tsx
index 453100672..c0cc85f25 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceSelectAnswerValueSetItem.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/OpenChoiceItems/OpenChoiceSelectAnswerValueSetItem.tsx
@@ -19,7 +19,7 @@ import type { AutocompleteChangeReason } from '@mui/material';
import type { Coding } from 'fhir/r4';
import useReadOnly from '../../../hooks/useReadOnly';
import useRenderingExtensions from '../../../hooks/useRenderingExtensions';
-import useValidationFeedback from '../../../hooks/useValidationFeedback';
+import useValidationFeedbackSeverity from '../../../hooks/useValidationFeedbackSeverity';
import useValueSetCodings from '../../../hooks/useValueSetCodings';
import type { BaseItemProps } from '../../../interfaces/renderProps.interface';
import { useQuestionnaireStore } from '../../../stores';
@@ -48,7 +48,7 @@ function OpenChoiceSelectAnswerValueSetItem(props: BaseItemProps) {
const readOnly = useReadOnly(qItem, parentIsReadOnly);
// Perform validation checks
- const feedback = useValidationFeedback(qItem, feedbackFromParent);
+ const { feedback, feedbackSeverity } = useValidationFeedbackSeverity(qItem, feedbackFromParent);
const { displayInstructions } = useRenderingExtensions(qItem);
const instructionsId = getInstructionsId(qItem, displayInstructions, !!feedback);
@@ -130,6 +130,7 @@ function OpenChoiceSelectAnswerValueSetItem(props: BaseItemProps) {
valueSelect={valueSelect}
terminologyError={terminologyError}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
isTabled={isTabled}
renderingExtensions={renderingExtensions}
readOnly={readOnly}
@@ -157,6 +158,7 @@ function OpenChoiceSelectAnswerValueSetItem(props: BaseItemProps) {
valueSelect={valueSelect}
terminologyError={terminologyError}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
isTabled={isTabled}
renderingExtensions={renderingExtensions}
readOnly={readOnly}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/QuantityItem/QuantityField.tsx b/packages/smart-forms-renderer/src/components/FormComponents/QuantityItem/QuantityField.tsx
index 929eee4d7..46de368b0 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/QuantityItem/QuantityField.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/QuantityItem/QuantityField.tsx
@@ -29,6 +29,7 @@ interface QuantityFieldProps extends PropsWithIsTabledAttribute {
itemText?: string;
input: string;
feedback: string;
+ feedbackSeverity?: 'error' | 'warning';
displayPrompt: string;
displayUnit: string;
entryFormat: string;
@@ -45,6 +46,7 @@ function QuantityField(props: QuantityFieldProps) {
itemText,
input,
feedback,
+ feedbackSeverity,
displayPrompt,
displayUnit,
entryFormat,
@@ -76,7 +78,7 @@ function QuantityField(props: QuantityFieldProps) {
onInputChange(event.target.value)}
disabled={readOnly && readOnlyVisualStyle === 'disabled'}
placeholder={placeholderText}
@@ -91,6 +93,9 @@ function QuantityField(props: QuantityFieldProps) {
...(ariaLabel && { 'aria-label': ariaLabel }),
...(instructionsId && { 'aria-describedby': instructionsId })
},
+ formHelperText: {
+ sx: feedbackSeverity === 'warning' && !!feedback ? { color: 'warning.main' } : undefined
+ },
input: {
readOnly: readOnly && readOnlyVisualStyle === 'readonly',
endAdornment: (
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/QuantityItem/QuantityItem.tsx b/packages/smart-forms-renderer/src/components/FormComponents/QuantityItem/QuantityItem.tsx
index be303fb81..a6523068a 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/QuantityItem/QuantityItem.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/QuantityItem/QuantityItem.tsx
@@ -19,7 +19,7 @@ import { useCallback, useMemo, useState } from 'react';
import type { Quantity, QuestionnaireItemAnswerOption } from 'fhir/r4';
import debounce from 'lodash.debounce';
import useReadOnly from '../../../hooks/useReadOnly';
-import useValidationFeedback from '../../../hooks/useValidationFeedback';
+import useValidationFeedbackSeverity from '../../../hooks/useValidationFeedbackSeverity';
import useRenderingExtensions from '../../../hooks/useRenderingExtensions';
import type { BaseItemProps } from '../../../interfaces/renderProps.interface';
import { useQuestionnaireStore } from '../../../stores';
@@ -113,7 +113,7 @@ function QuantityItem(props: BaseItemProps) {
const readOnly = useReadOnly(qItem, parentIsReadOnly);
// Perform validation checks
- const feedback = useValidationFeedback(qItem, feedbackFromParent);
+ const { feedback, feedbackSeverity } = useValidationFeedbackSeverity(qItem, feedbackFromParent);
const { displayInstructions } = useRenderingExtensions(qItem);
@@ -211,6 +211,7 @@ function QuantityItem(props: BaseItemProps) {
itemText={qItem.text}
input={valueInput}
feedback={feedback ?? ''}
+ feedbackSeverity={feedbackSeverity}
displayPrompt={displayPrompt}
displayUnit={displayUnit}
entryFormat={entryFormat}
@@ -265,6 +266,7 @@ function QuantityItem(props: BaseItemProps) {
itemText={qItem.text}
input={valueInput}
feedback={feedback ?? ''}
+ feedbackSeverity={feedbackSeverity}
displayPrompt={displayPrompt}
displayUnit={displayUnit}
entryFormat={entryFormat}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/SliderItem/SliderField.tsx b/packages/smart-forms-renderer/src/components/FormComponents/SliderItem/SliderField.tsx
index 8c274afac..869e4a922 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/SliderItem/SliderField.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/SliderItem/SliderField.tsx
@@ -37,6 +37,7 @@ interface SliderFieldProps extends PropsWithIsTabledAttribute {
stepValue: number;
isInteracted: boolean;
feedback: string;
+ feedbackSeverity?: 'error' | 'warning';
readOnly: boolean;
instructionsId?: string;
onValueChange: (newValue: number) => void;
@@ -55,6 +56,7 @@ function SliderField(props: SliderFieldProps) {
maxLabel,
isInteracted,
feedback,
+ feedbackSeverity,
readOnly,
instructionsId,
isTabled,
@@ -109,7 +111,12 @@ function SliderField(props: SliderFieldProps) {
/>
- {feedback ? {feedback} : null}
+ {feedback ? (
+
+ {feedback}
+
+ ) : null}
>
);
}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/SliderItem/SliderItem.tsx b/packages/smart-forms-renderer/src/components/FormComponents/SliderItem/SliderItem.tsx
index 75ef608cd..3fa964d51 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/SliderItem/SliderItem.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/SliderItem/SliderItem.tsx
@@ -25,7 +25,7 @@ import SliderField from './SliderField';
import useSliderExtensions from '../../../hooks/useSliderExtensions';
import Box from '@mui/material/Box';
import { useQuestionnaireStore } from '../../../stores';
-import useValidationFeedback from '../../../hooks/useValidationFeedback';
+import useValidationFeedbackSeverity from '../../../hooks/useValidationFeedbackSeverity';
import useRenderingExtensions from '../../../hooks/useRenderingExtensions';
import ItemLabel from '../ItemParts/ItemLabel';
@@ -60,7 +60,7 @@ function SliderItem(props: BaseItemProps) {
const readOnly = useReadOnly(qItem, parentIsReadOnly);
- const feedback = useValidationFeedback(qItem, feedbackFromParent);
+ const { feedback, feedbackSeverity } = useValidationFeedbackSeverity(qItem, feedbackFromParent);
const { displayInstructions } = useRenderingExtensions(qItem);
const instructionsId =
displayInstructions && !feedback ? `instructions-${qItem.linkId}` : undefined;
@@ -88,6 +88,7 @@ function SliderItem(props: BaseItemProps) {
maxLabel={maxLabel}
isInteracted={isInteracted}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
readOnly={readOnly}
isTabled={isTabled}
instructionsId={instructionsId}
@@ -122,6 +123,7 @@ function SliderItem(props: BaseItemProps) {
maxLabel={maxLabel}
isInteracted={isInteracted}
feedback={feedback}
+ feedbackSeverity={feedbackSeverity}
readOnly={readOnly}
isTabled={isTabled}
instructionsId={instructionsId}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/StringItem/StringField.tsx b/packages/smart-forms-renderer/src/components/FormComponents/StringItem/StringField.tsx
index 3fb3b1306..7638d9636 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/StringItem/StringField.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/StringItem/StringField.tsx
@@ -31,6 +31,7 @@ interface StringFieldProps extends PropsWithIsTabledAttribute {
qItem: QuestionnaireItem;
input: string;
feedback: string;
+ feedbackSeverity?: 'error' | 'warning';
renderingExtensions: RenderingExtensions;
readOnly: boolean;
calcExpUpdated: boolean;
@@ -44,6 +45,7 @@ function StringField(props: StringFieldProps) {
qItem,
input,
feedback,
+ feedbackSeverity,
renderingExtensions,
readOnly,
isTabled,
@@ -66,7 +68,7 @@ function StringField(props: StringFieldProps) {
textFieldWidth={textFieldWidth}
isTabled={isTabled}
value={input}
- error={!!feedback}
+ error={!!feedback && feedbackSeverity !== 'warning'}
onChange={(event) => onInputChange(event.target.value)}
placeholder={entryFormat || displayPrompt}
disabled={readOnly && readOnlyVisualStyle === 'disabled'}
@@ -94,6 +96,9 @@ function StringField(props: StringFieldProps) {
},
htmlInput: {
...(instructionsId && { 'aria-describedby': instructionsId })
+ },
+ formHelperText: {
+ sx: feedbackSeverity === 'warning' && !!feedback ? { color: 'warning.main' } : undefined
}
}}
helperText={{feedback}}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/StringItem/StringItem.tsx b/packages/smart-forms-renderer/src/components/FormComponents/StringItem/StringItem.tsx
index 1b62257aa..51e8fe829 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/StringItem/StringItem.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/StringItem/StringItem.tsx
@@ -19,7 +19,7 @@ import type { QuestionnaireResponseItem } from 'fhir/r4';
import debounce from 'lodash.debounce';
import { useCallback, useState } from 'react';
import useReadOnly from '../../../hooks/useReadOnly';
-import useValidationFeedback from '../../../hooks/useValidationFeedback';
+import useValidationFeedbackSeverity from '../../../hooks/useValidationFeedbackSeverity';
import type { BaseItemProps } from '../../../interfaces/renderProps.interface';
import { useQuestionnaireStore } from '../../../stores';
import { DEBOUNCE_DURATION } from '../../../utils/debounce';
@@ -57,7 +57,7 @@ function StringItem(props: BaseItemProps) {
const readOnly = useReadOnly(qItem, parentIsReadOnly);
// Perform validation checks
- const feedback = useValidationFeedback(qItem, feedbackFromParent);
+ const { feedback, feedbackSeverity } = useValidationFeedbackSeverity(qItem, feedbackFromParent);
// Get instructions ID for aria-describedby
const { displayInstructions } = useRenderingExtensions(qItem);
@@ -109,6 +109,7 @@ function StringItem(props: BaseItemProps) {
qItem={qItem}
input={input}
feedback={feedback ?? ''}
+ feedbackSeverity={feedbackSeverity}
renderingExtensions={renderingExtensions}
readOnly={readOnly}
calcExpUpdated={calcExpUpdated}
@@ -134,6 +135,7 @@ function StringItem(props: BaseItemProps) {
qItem={qItem}
input={input}
feedback={feedback ?? ''}
+ feedbackSeverity={feedbackSeverity}
renderingExtensions={renderingExtensions}
readOnly={readOnly}
calcExpUpdated={calcExpUpdated}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/TextItem/TextField.tsx b/packages/smart-forms-renderer/src/components/FormComponents/TextItem/TextField.tsx
index 0d83f63ac..d19510635 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/TextItem/TextField.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/TextItem/TextField.tsx
@@ -30,6 +30,7 @@ interface TextFieldProps {
qItem: QuestionnaireItem;
input: string;
feedback: string;
+ feedbackSeverity?: 'error' | 'warning';
renderingExtensions: RenderingExtensions;
readOnly: boolean;
calcExpUpdated: boolean;
@@ -43,6 +44,7 @@ function TextField(props: TextFieldProps) {
qItem,
input,
feedback,
+ feedbackSeverity,
renderingExtensions,
readOnly,
calcExpUpdated,
@@ -60,7 +62,7 @@ function TextField(props: TextFieldProps) {
onInputChange(event.target.value)}
disabled={readOnly && readOnlyVisualStyle === 'disabled'}
placeholder={entryFormat || displayPrompt}
@@ -85,6 +87,9 @@ function TextField(props: TextFieldProps) {
},
htmlInput: {
...(instructionsId && { 'aria-describedby': instructionsId })
+ },
+ formHelperText: {
+ sx: feedbackSeverity === 'warning' && !!feedback ? { color: 'warning.main' } : undefined
}
}}
helperText={{feedback}}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/TextItem/TextItem.tsx b/packages/smart-forms-renderer/src/components/FormComponents/TextItem/TextItem.tsx
index e2b73c26e..867113077 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/TextItem/TextItem.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/TextItem/TextItem.tsx
@@ -17,7 +17,7 @@
import { useCallback, useState } from 'react';
import type { BaseItemProps } from '../../../interfaces/renderProps.interface';
-import useValidationFeedback from '../../../hooks/useValidationFeedback';
+import useValidationFeedbackSeverity from '../../../hooks/useValidationFeedbackSeverity';
import debounce from 'lodash.debounce';
import { createEmptyQrItem, getQRItemId } from '../../../utils/qrItem';
import { DEBOUNCE_DURATION } from '../../../utils/debounce';
@@ -55,7 +55,7 @@ function TextItem(props: BaseItemProps) {
const readOnly = useReadOnly(qItem, parentIsReadOnly);
// Perform validation checks
- const feedback = useValidationFeedback(qItem, feedbackFromParent);
+ const { feedback, feedbackSeverity } = useValidationFeedbackSeverity(qItem, feedbackFromParent);
// Get instructions ID for aria-describedby
const { displayInstructions } = useRenderingExtensions(qItem);
@@ -107,6 +107,7 @@ function TextItem(props: BaseItemProps) {
qItem={qItem}
input={input}
feedback={feedback ?? ''}
+ feedbackSeverity={feedbackSeverity}
renderingExtensions={renderingExtensions}
readOnly={readOnly}
calcExpUpdated={calcExpUpdated}
@@ -132,6 +133,7 @@ function TextItem(props: BaseItemProps) {
qItem={qItem}
input={input}
feedback={feedback ?? ''}
+ feedbackSeverity={feedbackSeverity}
renderingExtensions={renderingExtensions}
readOnly={readOnly}
calcExpUpdated={calcExpUpdated}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/UrlItem/UrlField.tsx b/packages/smart-forms-renderer/src/components/FormComponents/UrlItem/UrlField.tsx
index 857983e6e..ed5106b6c 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/UrlItem/UrlField.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/UrlItem/UrlField.tsx
@@ -28,6 +28,7 @@ interface UrlFieldProps extends PropsWithIsTabledAttribute {
itemType: string;
input: string;
feedback: string;
+ feedbackSeverity?: 'error' | 'warning';
displayPrompt: string;
displayUnit: string;
entryFormat: string;
@@ -42,6 +43,7 @@ function UrlField(props: UrlFieldProps) {
itemType,
input,
feedback,
+ feedbackSeverity,
displayPrompt,
displayUnit,
entryFormat,
@@ -62,7 +64,7 @@ function UrlField(props: UrlFieldProps) {
textFieldWidth={textFieldWidth}
isTabled={isTabled}
value={input}
- error={!!feedback}
+ error={!!feedback && feedbackSeverity !== 'warning'}
onChange={(event) => onInputChange(event.target.value)}
placeholder={entryFormat || displayPrompt}
disabled={readOnly && readOnlyVisualStyle === 'disabled'}
@@ -84,6 +86,9 @@ function UrlField(props: UrlFieldProps) {
},
htmlInput: {
...(instructionsId && { 'aria-describedby': instructionsId })
+ },
+ formHelperText: {
+ sx: feedbackSeverity === 'warning' && !!feedback ? { color: 'warning.main' } : undefined
}
}}
helperText={feedback}
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/UrlItem/UrlItem.tsx b/packages/smart-forms-renderer/src/components/FormComponents/UrlItem/UrlItem.tsx
index 21a842e94..46ef3011e 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/UrlItem/UrlItem.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/UrlItem/UrlItem.tsx
@@ -17,7 +17,7 @@
import React, { useCallback, useState } from 'react';
import type { BaseItemProps } from '../../../interfaces/renderProps.interface';
-import useValidationFeedback from '../../../hooks/useValidationFeedback';
+import useValidationFeedbackSeverity from '../../../hooks/useValidationFeedbackSeverity';
import useRenderingExtensions from '../../../hooks/useRenderingExtensions';
import debounce from 'lodash.debounce';
import { createEmptyQrItem, getQRItemId } from '../../../utils/qrItem';
@@ -58,7 +58,7 @@ function UrlItem(props: BaseItemProps) {
const readOnly = useReadOnly(qItem, parentIsReadOnly);
// Perform validation checks
- const feedback = useValidationFeedback(qItem, feedbackFromParent);
+ const { feedback, feedbackSeverity } = useValidationFeedbackSeverity(qItem, feedbackFromParent);
const { displayInstructions } = useRenderingExtensions(qItem);
@@ -95,6 +95,7 @@ function UrlItem(props: BaseItemProps) {
itemType={qItem.type}
input={input}
feedback={feedback ?? ''}
+ feedbackSeverity={feedbackSeverity}
displayPrompt={displayPrompt}
displayUnit={displayUnit}
entryFormat={entryFormat}
@@ -121,6 +122,7 @@ function UrlItem(props: BaseItemProps) {
itemType={qItem.type}
input={input}
feedback={feedback ?? ''}
+ feedbackSeverity={feedbackSeverity}
displayPrompt={displayPrompt}
displayUnit={displayUnit}
entryFormat={entryFormat}
diff --git a/packages/smart-forms-renderer/src/hooks/useValidationFeedback.ts b/packages/smart-forms-renderer/src/hooks/useValidationFeedback.ts
index 205970bc4..380654954 100644
--- a/packages/smart-forms-renderer/src/hooks/useValidationFeedback.ts
+++ b/packages/smart-forms-renderer/src/hooks/useValidationFeedback.ts
@@ -16,187 +16,19 @@
*/
import type { QuestionnaireItem } from 'fhir/r4';
-import {
- getMaxQuantityValue,
- getMaxQuantityValueFeedback,
- getMaxValue,
- getMaxValueFeedback,
- getMinQuantityValue,
- getMinQuantityValueFeedback,
- getMinValue,
- getMinValueFeedback,
- getRegexValidation,
- getRequiredFeedback
-} from '../utils/extensions';
-import { useQuestionnaireResponseStore, useQuestionnaireStore } from '../stores';
-import { structuredDataCapture } from 'fhir-sdc-helpers';
+import useValidationFeedbackSeverity from './useValidationFeedbackSeverity';
+/**
+ * @deprecated Use {@link useValidationFeedbackSeverity} instead.
+ * This wrapper exists for backward compatibility and will be removed in the next major release.
+ * Switching to the new hook lets field components distinguish warning-level feedback
+ * (advisory, amber) from error-level feedback (blocking, red).
+ */
function useValidationFeedback(
qItem: QuestionnaireItem,
feedbackFromParent: string | undefined
): string {
- const invalidItems = useQuestionnaireResponseStore.use.invalidItems();
- const requiredItemsIsHighlighted = useQuestionnaireResponseStore.use.requiredItemsIsHighlighted();
-
- // Target constraint-based validation
- const targetConstraints = useQuestionnaireStore.use.targetConstraints();
- const targetConstraintLinkIds = useQuestionnaireStore.use.targetConstraintLinkIds();
- const targetConstraintKeys = targetConstraintLinkIds[qItem.linkId];
- if (targetConstraintKeys && targetConstraintKeys.length > 0) {
- for (const targetConstraintKey of targetConstraintKeys) {
- const targetConstraint = targetConstraints[targetConstraintKey];
- if (targetConstraint) {
- const { isInvalid, human } = targetConstraint;
- if (isInvalid) {
- return human;
- }
- }
- }
- }
-
- // Feedback from parent
- if (feedbackFromParent) {
- return feedbackFromParent;
- }
-
- // Feedback from current item from QR invalidItems
- const invalidOperationOutcome = invalidItems[qItem.linkId];
-
- // Invalid items are not present, so no feedback returned
- if (!invalidOperationOutcome) {
- return '';
- }
-
- // There is an invalidOperationOutcome but no issues, something is wrong with the validation - should never happen
- if (!invalidOperationOutcome.issue || invalidOperationOutcome.issue.length === 0) {
- return 'Input is invalid but no specific issues are found. Please report this at https://github.com/aehrc/smart-forms/issues.';
- }
-
- // Required-based validation
- // User needs to manually invoke required items to be highlighted
- if (requiredItemsIsHighlighted) {
- const requiredIssue = invalidOperationOutcome.issue.find((issue) => issue.code === 'required');
- if (requiredIssue) {
- const requiredFeedback = getRequiredFeedback(qItem);
- return requiredFeedback ?? 'This field is required.';
- }
- }
-
- // Iterate through the issues but we only return the first validation feedback we find.
- // Once that's resolved, the subsequent feedbacks will be returned.
- for (const issue of invalidOperationOutcome.issue) {
- const validationCode = issue?.details?.coding?.[0].code;
-
- // If no validation code is provided, something is wrong with the validation - should never happen
- if (!validationCode) {
- return 'Input is invalid but no specific issues are found. Please report this at https://github.com/aehrc/smart-forms/issues.';
- }
-
- // http://hl7.org/fhir/StructureDefinition/regex
- if (validationCode === 'regex') {
- const regexValidation = getRegexValidation(qItem);
- if (regexValidation) {
- return `Input should match the specified regex: ${regexValidation.expression}`;
- }
-
- return 'Input should match the specified regex.';
- }
-
- // http://hl7.org/fhir/StructureDefinition/minLength
- if (validationCode === 'minLength') {
- const minLength = structuredDataCapture.getMinLength(qItem);
- if (typeof minLength === 'number') {
- return `Enter at least ${minLength} characters.`;
- }
-
- return 'Input is below the minimum character limit.';
- }
-
- // Questionnaire.item.maxLength
- if (validationCode === 'maxLength') {
- const maxLength = qItem.maxLength;
- if (typeof maxLength === 'number') {
- return `Enter no more than ${maxLength} characters.`;
- }
-
- return 'Input is above the maximum character limit.';
- }
-
- // http://hl7.org/fhir/StructureDefinition/maxDecimalPlaces
- if (validationCode === 'maxDecimalPlaces') {
- const maxDecimalPlaces = structuredDataCapture.getMaxDecimalPlaces(qItem);
- if (typeof maxDecimalPlaces === 'number') {
- return `Enter a number with no more than ${maxDecimalPlaces} decimal places.`;
- }
-
- return 'Input has too many decimal places.';
- }
-
- // http://hl7.org/fhir/StructureDefinition/minValue
- if (validationCode === 'minValue') {
- const minValueFeedback = getMinValueFeedback(qItem);
- if (minValueFeedback) {
- return minValueFeedback;
- }
-
- const minValue = getMinValue(qItem);
- if (typeof minValue === 'string' || typeof minValue === 'number') {
- return `Enter a value greater than or equal to ${minValue}.`;
- }
-
- return 'Input is less than the minimum value allowed.';
- }
-
- // http://hl7.org/fhir/StructureDefinition/maxValue
- if (validationCode === 'maxValue') {
- const maxValueFeedback = getMaxValueFeedback(qItem);
- if (maxValueFeedback) {
- return maxValueFeedback;
- }
-
- const maxValue = getMaxValue(qItem);
- if (typeof maxValue === 'string' || typeof maxValue === 'number') {
- return `Enter a value less than or equal to ${maxValue}.`;
- }
-
- return 'Input exceeds the maximum value allowed.';
- }
-
- // http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-minQuantity
- if (validationCode === 'minQuantityValue') {
- const minQuantityFeedback = getMinQuantityValueFeedback(qItem);
- if (minQuantityFeedback) {
- return minQuantityFeedback;
- }
-
- const minQuantityValue = getMinQuantityValue(qItem);
- if (typeof minQuantityValue === 'number') {
- return `Enter a quantity greater than or equal to ${minQuantityValue}.`;
- }
-
- return 'Input is less than the minimum quantity allowed.';
- }
-
- // http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-maxQuantity
- if (validationCode === 'maxQuantityValue') {
- const maxQuantityFeedback = getMaxQuantityValueFeedback(qItem);
- if (maxQuantityFeedback) {
- return maxQuantityFeedback;
- }
-
- const maxQuantityValue = getMaxQuantityValue(qItem);
- if (typeof maxQuantityValue === 'number') {
- return `Enter a quantity less than or equal to ${maxQuantityValue}.`;
- }
-
- return 'Input exceeds the maximum quantity allowed.';
- }
-
- // No specific issue code, continue to the next issue
- }
-
- // No specific issue code from all issues, fallback to empty string
- return '';
+ return useValidationFeedbackSeverity(qItem, feedbackFromParent).feedback;
}
export default useValidationFeedback;
diff --git a/packages/smart-forms-renderer/src/hooks/useValidationFeedbackSeverity.ts b/packages/smart-forms-renderer/src/hooks/useValidationFeedbackSeverity.ts
new file mode 100644
index 000000000..9ae6f38d2
--- /dev/null
+++ b/packages/smart-forms-renderer/src/hooks/useValidationFeedbackSeverity.ts
@@ -0,0 +1,247 @@
+/*
+ * Copyright 2025 Commonwealth Scientific and Industrial Research
+ * Organisation (CSIRO) ABN 41 687 119 230.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type { QuestionnaireItem } from 'fhir/r4';
+import {
+ getMaxQuantityValue,
+ getMaxQuantityValueFeedback,
+ getMaxValue,
+ getMaxValueFeedback,
+ getMinQuantityValue,
+ getMinQuantityValueFeedback,
+ getMinValue,
+ getMinValueFeedback,
+ getRegexValidation,
+ getRequiredFeedback
+} from '../utils/extensions';
+import { useQuestionnaireResponseStore, useQuestionnaireStore } from '../stores';
+import { structuredDataCapture } from 'fhir-sdc-helpers';
+
+export type FeedbackSeverity = 'error' | 'warning';
+
+export interface ValidationFeedbackWithSeverity {
+ feedback: string;
+ feedbackSeverity: FeedbackSeverity;
+}
+
+/**
+ * Returns both the human-readable validation feedback message and its severity
+ * (`'error'` or `'warning'`), allowing field components to style advisory warnings
+ * differently from blocking errors.
+ *
+ * Use this hook in place of the deprecated {@link useValidationFeedback}.
+ */
+function useValidationFeedbackSeverity(
+ qItem: QuestionnaireItem,
+ feedbackFromParent: string | undefined
+): ValidationFeedbackWithSeverity {
+ const invalidItems = useQuestionnaireResponseStore.use.invalidItems();
+ const requiredItemsIsHighlighted = useQuestionnaireResponseStore.use.requiredItemsIsHighlighted();
+
+ // Target constraint-based validation — severity comes from the constraint definition
+ const targetConstraints = useQuestionnaireStore.use.targetConstraints();
+ const targetConstraintLinkIds = useQuestionnaireStore.use.targetConstraintLinkIds();
+ const targetConstraintKeys = targetConstraintLinkIds[qItem.linkId];
+ if (targetConstraintKeys && targetConstraintKeys.length > 0) {
+ for (const targetConstraintKey of targetConstraintKeys) {
+ const targetConstraint = targetConstraints[targetConstraintKey];
+ if (targetConstraint) {
+ const { isInvalid, human, severityCode } = targetConstraint;
+ if (isInvalid) {
+ return { feedback: human, feedbackSeverity: severityCode ?? 'error' };
+ }
+ }
+ }
+ }
+
+ // Feedback from parent — parent groups don't carry a severity, treat as error
+ if (feedbackFromParent) {
+ return { feedback: feedbackFromParent, feedbackSeverity: 'error' };
+ }
+
+ // Feedback from current item from QR invalidItems
+ const invalidOperationOutcome = invalidItems[qItem.linkId];
+
+ // No invalid items — no feedback
+ if (!invalidOperationOutcome) {
+ return { feedback: '', feedbackSeverity: 'error' };
+ }
+
+ // OperationOutcome present but no issues — internal error, should never happen
+ if (!invalidOperationOutcome.issue || invalidOperationOutcome.issue.length === 0) {
+ return {
+ feedback:
+ 'Input is invalid but no specific issues are found. Please report this at https://github.com/aehrc/smart-forms/issues.',
+ feedbackSeverity: 'error'
+ };
+ }
+
+ // Required-based validation — user must manually invoke required highlighting
+ if (requiredItemsIsHighlighted) {
+ const requiredIssue = invalidOperationOutcome.issue.find((issue) => issue.code === 'required');
+ if (requiredIssue) {
+ const requiredFeedback = getRequiredFeedback(qItem);
+ const severity: FeedbackSeverity = requiredIssue.severity === 'warning' ? 'warning' : 'error';
+ return {
+ feedback: requiredFeedback ?? 'This field is required.',
+ feedbackSeverity: severity
+ };
+ }
+ }
+
+ // Iterate through issues; return the first recognisable validation feedback together with
+ // the severity stored on that issue (set by createValidationOperationOutcomeIssue).
+ for (const issue of invalidOperationOutcome.issue) {
+ const validationCode = issue?.details?.coding?.[0].code;
+
+ if (!validationCode) {
+ return {
+ feedback:
+ 'Input is invalid but no specific issues are found. Please report this at https://github.com/aehrc/smart-forms/issues.',
+ feedbackSeverity: 'error'
+ };
+ }
+
+ const severity: FeedbackSeverity = issue.severity === 'warning' ? 'warning' : 'error';
+
+ if (validationCode === 'regex') {
+ const regexValidation = getRegexValidation(qItem);
+ if (regexValidation) {
+ return {
+ feedback: `Input should match the specified regex: ${regexValidation.expression}`,
+ feedbackSeverity: severity
+ };
+ }
+ return { feedback: 'Input should match the specified regex.', feedbackSeverity: severity };
+ }
+
+ if (validationCode === 'minLength') {
+ const minLength = structuredDataCapture.getMinLength(qItem);
+ if (typeof minLength === 'number') {
+ return {
+ feedback: `Enter at least ${minLength} characters.`,
+ feedbackSeverity: severity
+ };
+ }
+ return {
+ feedback: 'Input is below the minimum character limit.',
+ feedbackSeverity: severity
+ };
+ }
+
+ if (validationCode === 'maxLength') {
+ const maxLength = qItem.maxLength;
+ if (typeof maxLength === 'number') {
+ return {
+ feedback: `Enter no more than ${maxLength} characters.`,
+ feedbackSeverity: severity
+ };
+ }
+ return {
+ feedback: 'Input is above the maximum character limit.',
+ feedbackSeverity: severity
+ };
+ }
+
+ if (validationCode === 'maxDecimalPlaces') {
+ const maxDecimalPlaces = structuredDataCapture.getMaxDecimalPlaces(qItem);
+ if (typeof maxDecimalPlaces === 'number') {
+ return {
+ feedback: `Enter a number with no more than ${maxDecimalPlaces} decimal places.`,
+ feedbackSeverity: severity
+ };
+ }
+ return { feedback: 'Input has too many decimal places.', feedbackSeverity: severity };
+ }
+
+ if (validationCode === 'minValue') {
+ const minValueFeedback = getMinValueFeedback(qItem);
+ if (minValueFeedback) {
+ return { feedback: minValueFeedback, feedbackSeverity: severity };
+ }
+ const minValue = getMinValue(qItem);
+ if (typeof minValue === 'string' || typeof minValue === 'number') {
+ return {
+ feedback: `Enter a value greater than or equal to ${minValue}.`,
+ feedbackSeverity: severity
+ };
+ }
+ return {
+ feedback: 'Input is less than the minimum value allowed.',
+ feedbackSeverity: severity
+ };
+ }
+
+ if (validationCode === 'maxValue') {
+ const maxValueFeedback = getMaxValueFeedback(qItem);
+ if (maxValueFeedback) {
+ return { feedback: maxValueFeedback, feedbackSeverity: severity };
+ }
+ const maxValue = getMaxValue(qItem);
+ if (typeof maxValue === 'string' || typeof maxValue === 'number') {
+ return {
+ feedback: `Enter a value less than or equal to ${maxValue}.`,
+ feedbackSeverity: severity
+ };
+ }
+ return {
+ feedback: 'Input exceeds the maximum value allowed.',
+ feedbackSeverity: severity
+ };
+ }
+
+ if (validationCode === 'minQuantityValue') {
+ const minQuantityFeedback = getMinQuantityValueFeedback(qItem);
+ if (minQuantityFeedback) {
+ return { feedback: minQuantityFeedback, feedbackSeverity: severity };
+ }
+ const minQuantityValue = getMinQuantityValue(qItem);
+ if (typeof minQuantityValue === 'number') {
+ return {
+ feedback: `Enter a quantity greater than or equal to ${minQuantityValue}.`,
+ feedbackSeverity: severity
+ };
+ }
+ return {
+ feedback: 'Input is less than the minimum quantity allowed.',
+ feedbackSeverity: severity
+ };
+ }
+
+ if (validationCode === 'maxQuantityValue') {
+ const maxQuantityFeedback = getMaxQuantityValueFeedback(qItem);
+ if (maxQuantityFeedback) {
+ return { feedback: maxQuantityFeedback, feedbackSeverity: severity };
+ }
+ const maxQuantityValue = getMaxQuantityValue(qItem);
+ if (typeof maxQuantityValue === 'number') {
+ return {
+ feedback: `Enter a quantity less than or equal to ${maxQuantityValue}.`,
+ feedbackSeverity: severity
+ };
+ }
+ return {
+ feedback: 'Input exceeds the maximum quantity allowed.',
+ feedbackSeverity: severity
+ };
+ }
+ }
+
+ return { feedback: '', feedbackSeverity: 'error' };
+}
+
+export default useValidationFeedbackSeverity;
diff --git a/packages/smart-forms-renderer/src/test/useValidationFeedbackSeverity.test.ts b/packages/smart-forms-renderer/src/test/useValidationFeedbackSeverity.test.ts
new file mode 100644
index 000000000..7ad4d58c8
--- /dev/null
+++ b/packages/smart-forms-renderer/src/test/useValidationFeedbackSeverity.test.ts
@@ -0,0 +1,268 @@
+///
+///
+
+/*
+ * Copyright 2025 Commonwealth Scientific and Industrial Research
+ * Organisation (CSIRO) ABN 41 687 119 230.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { renderHook } from '@testing-library/react';
+import type { QuestionnaireItem, OperationOutcome } from 'fhir/r4';
+import useValidationFeedbackSeverity from '../hooks/useValidationFeedbackSeverity';
+import type { TargetConstraint } from '../interfaces/targetConstraint.interface';
+
+// Mock dependencies
+jest.mock('../utils/extensions', () => ({
+ getMaxQuantityValue: jest.fn(),
+ getMaxQuantityValueFeedback: jest.fn(),
+ getMaxValue: jest.fn(),
+ getMaxValueFeedback: jest.fn(),
+ getMinQuantityValue: jest.fn(),
+ getMinQuantityValueFeedback: jest.fn(),
+ getMinValue: jest.fn(),
+ getMinValueFeedback: jest.fn(),
+ getRegexValidation: jest.fn(),
+ getRequiredFeedback: jest.fn()
+}));
+
+jest.mock('fhir-sdc-helpers', () => ({
+ structuredDataCapture: {
+ getMinLength: jest.fn(),
+ getMaxDecimalPlaces: jest.fn()
+ }
+}));
+
+let mockInvalidItems: Record = {};
+let mockRequiredItemsIsHighlighted = false;
+let mockTargetConstraints: Record = {};
+let mockTargetConstraintLinkIds: Record = {};
+
+jest.mock('../stores', () => ({
+ useQuestionnaireResponseStore: {
+ use: {
+ invalidItems: () => mockInvalidItems,
+ requiredItemsIsHighlighted: () => mockRequiredItemsIsHighlighted
+ }
+ },
+ useQuestionnaireStore: {
+ use: {
+ targetConstraints: () => mockTargetConstraints,
+ targetConstraintLinkIds: () => mockTargetConstraintLinkIds
+ }
+ }
+}));
+
+import { getRequiredFeedback, getRegexValidation } from '../utils/extensions';
+
+const mockGetRequiredFeedback = getRequiredFeedback as jest.MockedFunction<
+ typeof getRequiredFeedback
+>;
+const mockGetRegexValidation = getRegexValidation as jest.MockedFunction;
+
+describe('useValidationFeedbackSeverity', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ mockInvalidItems = {};
+ mockRequiredItemsIsHighlighted = false;
+ mockTargetConstraints = {};
+ mockTargetConstraintLinkIds = {};
+ mockGetRequiredFeedback.mockReturnValue(null);
+ mockGetRegexValidation.mockReturnValue(undefined);
+ });
+
+ describe('no validation issues', () => {
+ it('returns empty feedback and error severity when there are no issues', () => {
+ const qItem: QuestionnaireItem = { linkId: 'test', type: 'string' };
+
+ const { result } = renderHook(() => useValidationFeedbackSeverity(qItem, undefined));
+
+ expect(result.current.feedback).toBe('');
+ expect(result.current.feedbackSeverity).toBe('error');
+ });
+ });
+
+ describe('target constraint severity', () => {
+ it('returns "error" severity when a target constraint with severityCode "error" is invalid', () => {
+ const qItem: QuestionnaireItem = { linkId: 'test', type: 'string' };
+
+ mockTargetConstraintLinkIds = { test: ['c1'] };
+ mockTargetConstraints = {
+ c1: {
+ key: 'c1',
+ severityCode: 'error',
+ isInvalid: true,
+ human: 'Value must not be empty',
+ valueExpression: { language: 'text/fhirpath', expression: 'true' }
+ }
+ };
+
+ const { result } = renderHook(() => useValidationFeedbackSeverity(qItem, undefined));
+
+ expect(result.current.feedback).toBe('Value must not be empty');
+ expect(result.current.feedbackSeverity).toBe('error');
+ });
+
+ it('returns "warning" severity when a target constraint with severityCode "warning" is invalid', () => {
+ const qItem: QuestionnaireItem = { linkId: 'test', type: 'string' };
+
+ mockTargetConstraintLinkIds = { test: ['c1'] };
+ mockTargetConstraints = {
+ c1: {
+ key: 'c1',
+ severityCode: 'warning',
+ isInvalid: true,
+ human: 'Consider adding a more specific value',
+ valueExpression: { language: 'text/fhirpath', expression: 'true' }
+ }
+ };
+
+ const { result } = renderHook(() => useValidationFeedbackSeverity(qItem, undefined));
+
+ expect(result.current.feedback).toBe('Consider adding a more specific value');
+ expect(result.current.feedbackSeverity).toBe('warning');
+ });
+
+ it('skips valid target constraints even if severity is warning', () => {
+ const qItem: QuestionnaireItem = { linkId: 'test', type: 'string' };
+
+ mockTargetConstraintLinkIds = { test: ['c1'] };
+ mockTargetConstraints = {
+ c1: {
+ key: 'c1',
+ severityCode: 'warning',
+ isInvalid: false,
+ human: 'Should not appear',
+ valueExpression: { language: 'text/fhirpath', expression: 'true' }
+ }
+ };
+
+ const { result } = renderHook(() => useValidationFeedbackSeverity(qItem, undefined));
+
+ expect(result.current.feedback).toBe('');
+ });
+ });
+
+ describe('feedbackFromParent severity', () => {
+ it('always returns "error" severity for feedbackFromParent', () => {
+ const qItem: QuestionnaireItem = { linkId: 'test', type: 'string' };
+
+ const { result } = renderHook(() =>
+ useValidationFeedbackSeverity(qItem, 'Parent group error')
+ );
+
+ expect(result.current.feedback).toBe('Parent group error');
+ expect(result.current.feedbackSeverity).toBe('error');
+ });
+ });
+
+ describe('OperationOutcome issue severity', () => {
+ it('returns "error" severity when the matching issue has severity "error"', () => {
+ const qItem: QuestionnaireItem = { linkId: 'test', type: 'string' };
+
+ mockInvalidItems = {
+ test: {
+ resourceType: 'OperationOutcome',
+ issue: [
+ {
+ severity: 'error',
+ code: 'invalid',
+ details: { coding: [{ code: 'regex' }] }
+ }
+ ]
+ }
+ };
+
+ mockGetRegexValidation.mockReturnValue(undefined);
+
+ const { result } = renderHook(() => useValidationFeedbackSeverity(qItem, undefined));
+
+ expect(result.current.feedback).toBe('Input should match the specified regex.');
+ expect(result.current.feedbackSeverity).toBe('error');
+ });
+
+ it('returns "warning" severity when the matching issue has severity "warning"', () => {
+ const qItem: QuestionnaireItem = { linkId: 'test', type: 'string' };
+
+ mockInvalidItems = {
+ test: {
+ resourceType: 'OperationOutcome',
+ issue: [
+ {
+ severity: 'warning',
+ code: 'invalid',
+ details: { coding: [{ code: 'regex' }] }
+ }
+ ]
+ }
+ };
+
+ mockGetRegexValidation.mockReturnValue(undefined);
+
+ const { result } = renderHook(() => useValidationFeedbackSeverity(qItem, undefined));
+
+ expect(result.current.feedback).toBe('Input should match the specified regex.');
+ expect(result.current.feedbackSeverity).toBe('warning');
+ });
+
+ it('returns "warning" severity for a required field issue marked as warning', () => {
+ const qItem: QuestionnaireItem = { linkId: 'test', type: 'string' };
+
+ mockRequiredItemsIsHighlighted = true;
+ mockInvalidItems = {
+ test: {
+ resourceType: 'OperationOutcome',
+ issue: [
+ {
+ severity: 'warning',
+ code: 'required',
+ details: { coding: [{ code: 'required' }] }
+ }
+ ]
+ }
+ };
+
+ mockGetRequiredFeedback.mockReturnValue('Advisory: this field is recommended');
+
+ const { result } = renderHook(() => useValidationFeedbackSeverity(qItem, undefined));
+
+ expect(result.current.feedback).toBe('Advisory: this field is recommended');
+ expect(result.current.feedbackSeverity).toBe('warning');
+ });
+ });
+
+ describe('backward-compatible feedback text', () => {
+ it('returns identical feedback text to the deprecated useValidationFeedback hook', async () => {
+ const qItem: QuestionnaireItem = { linkId: 'test', type: 'string', maxLength: 5 };
+
+ mockInvalidItems = {
+ test: {
+ resourceType: 'OperationOutcome',
+ issue: [
+ {
+ severity: 'error',
+ code: 'business-rule',
+ details: { coding: [{ code: 'maxLength' }] }
+ }
+ ]
+ }
+ };
+
+ const { result } = renderHook(() => useValidationFeedbackSeverity(qItem, undefined));
+
+ expect(result.current.feedback).toBe('Enter no more than 5 characters.');
+ expect(result.current.feedbackSeverity).toBe('error');
+ });
+ });
+});