diff --git a/src/components/inputs/number/number.component.tsx b/src/components/inputs/number/number.component.tsx index f33364ca..ecccc1dc 100644 --- a/src/components/inputs/number/number.component.tsx +++ b/src/components/inputs/number/number.component.tsx @@ -11,6 +11,28 @@ import { useFormProviderContext } from '../../../provider/form-provider'; import FieldLabel from '../../field-label/field-label.component'; import { isEmpty } from '../../../validators/form-validator'; + +const extractFieldUnitsAndRange = (concept) => { + if (!concept) { + return ''; + } + + const { hiAbsolute, lowAbsolute, units } = concept; + const displayUnit = units ? ` ${units}` : ''; + + const hasLowerLimit = lowAbsolute != null; + const hasUpperLimit = hiAbsolute != null; + + if (hasLowerLimit && hasUpperLimit) { + return ` (${lowAbsolute} - ${hiAbsolute} ${displayUnit})`; + } else if (hasUpperLimit) { + return ` (<= ${hiAbsolute} ${displayUnit})`; + } else if (hasLowerLimit) { + return ` (>= ${lowAbsolute} ${displayUnit})`; + } + return units ? ` (${displayUnit})` : ''; +}; + const NumberField: React.FC = ({ field, value, errors, warnings, setFieldValue }) => { const { t } = useTranslation(); const [lastBlurredValue, setLastBlurredValue] = useState(value); @@ -61,7 +83,7 @@ const NumberField: React.FC = ({ field, value, errors, warn id={field.id} invalid={errors.length > 0} invalidText={errors[0]?.message} - label={} + label={} max={Number(field.questionOptions.max) || undefined} min={Number(field.questionOptions.min) || undefined} name={field.id} diff --git a/src/components/inputs/number/number.test.tsx b/src/components/inputs/number/number.test.tsx index f1f04dac..c28b4f8f 100644 --- a/src/components/inputs/number/number.test.tsx +++ b/src/components/inputs/number/number.test.tsx @@ -22,6 +22,25 @@ const numberFieldMock = { readonly: false, }; +const numberFieldMockWithUnitsAndRange = { + label: 'Weight', + type: 'obs', + id: 'weight', + questionOptions: { + rendering: 'number', + }, + meta: { + concept: { + units: 'kg', + lowAbsolute: 0, + hiAbsolute: 200, + } + }, + isHidden: false, + isDisabled: false, + readonly: false, +}; + const renderNumberField = async (props) => { await act(() => render()); }; @@ -104,4 +123,15 @@ describe('NumberField Component', () => { const inputElement = screen.getByLabelText('Weight(kg):') as HTMLInputElement; expect(inputElement).toBeDisabled(); }); + + it('renders units and range', async () => { + await renderNumberField({ + field: numberFieldMockWithUnitsAndRange, + value: '', + errors: [], + warnings: [], + setFieldValue: jest.fn(), + }); + expect(screen.getByLabelText('Weight (0 - 200 kg)')).toBeInTheDocument(); + }); }); diff --git a/src/hooks/useConcepts.ts b/src/hooks/useConcepts.ts index d6f2d217..e844c2cc 100644 --- a/src/hooks/useConcepts.ts +++ b/src/hooks/useConcepts.ts @@ -5,7 +5,7 @@ import { type FetchResponse, type OpenmrsResource, openmrsFetch, restBaseUrl } f type ConceptFetchResponse = FetchResponse<{ results: Array }>; const conceptRepresentation = - 'custom:(uuid,display,conceptClass:(uuid,display),answers:(uuid,display),conceptMappings:(conceptReferenceTerm:(conceptSource:(name),code)))'; + 'custom:(units,lowAbsolute,hiAbsolute,uuid,display,conceptClass:(uuid,display),answers:(uuid,display),conceptMappings:(conceptReferenceTerm:(conceptSource:(name),code)))'; export function useConcepts(references: Set): { concepts: Array | undefined; diff --git a/src/hooks/useFormFieldsMeta.ts b/src/hooks/useFormFieldsMeta.ts index 973b7ae0..3f3e44ab 100644 --- a/src/hooks/useFormFieldsMeta.ts +++ b/src/hooks/useFormFieldsMeta.ts @@ -11,6 +11,20 @@ export function useFormFieldsMeta(rawFormFields: FormField[], concepts: OpenmrsR const matchingConcept = findConceptByReference(field.questionOptions.concept, concepts); field.questionOptions.concept = matchingConcept ? matchingConcept.uuid : field.questionOptions.concept; field.label = field.label ? field.label : matchingConcept?.display; + + if (matchingConcept) { + if (matchingConcept.lowAbsolute != undefined && matchingConcept.hiAbsolute != undefined) { + field.questionOptions.min = matchingConcept.lowAbsolute; + field.questionOptions.max = matchingConcept.hiAbsolute; + } + else if (matchingConcept.lowAbsolute != undefined) { + field.questionOptions.min = matchingConcept.lowAbsolute; + } + else if (matchingConcept.hiAbsolute != undefined) { + field.questionOptions.max = matchingConcept.hiAbsolute; + } + } + if ( codedTypes.includes(field.questionOptions.rendering) && !field.questionOptions.answers?.length &&