fhir-react 0.2.2 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/publish_npmjs.yml +20 -0
- package/.storybook/config.js +9 -3
- package/.storybook/presets.js +1 -0
- package/.storybook/preview-head.html +4 -0
- package/README.md +41 -13
- package/build/bootstrap-reboot.min.css +2 -22
- package/build/index.js +38 -3
- package/build/style.css +11 -227
- package/package.json +13 -4
- package/src/assets/common/chevron-right.svg +3 -0
- package/src/assets/containers/AllergyIntolerance/allergy-intolerance.svg +9 -0
- package/src/assets/containers/Appointment/appointment.svg +14 -0
- package/src/assets/containers/CarePlan/care-plan.svg +10 -0
- package/src/assets/containers/CareTeam/care-team.svg +10 -0
- package/src/assets/containers/Claim/claim.svg +6 -0
- package/src/assets/containers/ClaimResponse/claim-response.svg +7 -0
- package/src/assets/containers/Condition/condition.svg +11 -0
- package/src/assets/containers/Device/device.svg +8 -0
- package/src/assets/containers/DiagnosticReport/diagnostic-report.svg +14 -0
- package/src/assets/containers/DocumentReference/document-reference.svg +10 -0
- package/src/assets/containers/Encounter/encounter.svg +10 -0
- package/src/assets/containers/ExplanationOfBenefit/explanation-of-benefit.svg +3 -0
- package/src/assets/containers/FamilyMemberHistory/family-member-history.svg +7 -0
- package/src/assets/containers/Goal/goal.svg +11 -0
- package/src/assets/containers/Immunization/immunization.svg +7 -0
- package/src/assets/containers/List/list.svg +3 -0
- package/src/assets/containers/Location/location.svg +4 -0
- package/src/assets/containers/Medication/medication.svg +5 -0
- package/src/assets/containers/MedicationAdministration/medication-administration.svg +6 -0
- package/src/assets/containers/MedicationKnowledge/medication-knowledge.svg +11 -0
- package/src/assets/containers/MedicationStatement/medication-statement.svg +5 -0
- package/src/assets/containers/Observation/observation.svg +12 -0
- package/src/assets/containers/Practitioner/practitioner.svg +5 -0
- package/src/assets/containers/Procedure/procedure.svg +9 -0
- package/src/assets/containers/Questionnaire/questionnaire.svg +6 -0
- package/src/assets/containers/QuestionnaireResponse/questionnaire-response.svg +6 -0
- package/src/assets/containers/QustionnaireResponse/questionnaire-response.svg +6 -0
- package/src/assets/containers/ResearchStudy/research-study.svg +9 -0
- package/src/assets/containers/ResourceCategory/resource-placeholder.svg +3 -0
- package/src/components/containers/Accordion/Accordion.js +80 -0
- package/src/components/containers/Accordion/Accordion.stories.js +76 -0
- package/src/components/containers/Accordion/index.js +3 -0
- package/src/components/containers/ResourceContainer/ResourceContainer.css +0 -1
- package/src/components/containers/ResourceContainer/ResourceContainer.js +1 -1
- package/src/components/datatypes/AccountBalance/AccountBalance.js +33 -0
- package/src/components/datatypes/AccountBalance/index.js +3 -0
- package/src/components/datatypes/Annotation/Annotation.js +1 -1
- package/src/components/datatypes/Coding/Coding.js +1 -1
- package/src/components/datatypes/Date/Date.js +14 -4
- package/src/components/datatypes/DatePeriod/DatePeriod.js +38 -0
- package/src/components/datatypes/DatePeriod/index.js +3 -0
- package/src/components/datatypes/HeaderIcon/HeaderIcon.js +31 -0
- package/src/components/datatypes/HeaderIcon/index.js +3 -0
- package/src/components/datatypes/HumanName/HumanName.js +6 -21
- package/src/components/datatypes/Reference/Reference.js +3 -6
- package/src/components/resources/AdverseEvent/AdverseEvent.test.js +2 -2
- package/src/components/resources/AllergyIntolerance/AllergyIntolerance.test.js +4 -4
- package/src/components/resources/Appointment/Appointment.js +91 -65
- package/src/components/resources/Appointment/Appointment.test.js +3 -3
- package/src/components/resources/Bundle/Bundle.js +2 -2
- package/src/components/resources/Bundle/Bundle.stories.js +78 -12
- package/src/components/resources/Bundle/Bundle.test.js +3 -0
- package/src/components/resources/CarePlan/CarePlan.test.js +4 -4
- package/src/components/resources/CareTeam/CareTeam.js +13 -14
- package/src/components/resources/CareTeam/CareTeam.test.js +4 -4
- package/src/components/resources/Claim/Claim.test.js +6 -6
- package/src/components/resources/ClaimResponse/ClaimResponse.test.js +6 -6
- package/src/components/resources/Condition/Condition.js +63 -47
- package/src/components/resources/Condition/Condition.stories.js +41 -8
- package/src/components/resources/Condition/Condition.test.js +20 -14
- package/src/components/resources/DiagnosticReport/DiagnosticReport.test.js +5 -7
- package/src/components/resources/DocumentReference/DocumentReference.js +1 -1
- package/src/components/resources/DocumentReference/DocumentReference.test.js +3 -3
- package/src/components/resources/Encounter/Encounter.js +66 -36
- package/src/components/resources/Encounter/EncounterParticipants.js +2 -2
- package/src/components/resources/ExplanationOfBenefit/CareTeam.js +2 -2
- package/src/components/resources/ExplanationOfBenefit/Diagnosis.js +31 -5
- package/src/components/resources/ExplanationOfBenefit/ExplanationOfBenefit.js +272 -201
- package/src/components/resources/ExplanationOfBenefit/ExplanationOfBenefit.stories.js +12 -0
- package/src/components/resources/ExplanationOfBenefit/ExplanationOfBenefit.test.js +96 -62
- package/src/components/resources/ExplanationOfBenefit/Items.js +2 -2
- package/src/components/resources/ExplanationOfBenefit/PriceLabel.js +20 -0
- package/src/components/resources/ExplanationOfBenefit/Related.js +3 -3
- package/src/components/resources/ExplanationOfBenefit/SupportingInfo.js +14 -3
- package/src/components/resources/ExplanationOfBenefit/TotalGraph.js +68 -0
- package/src/components/resources/ExplanationOfBenefitGraph/ExplanationOfBenefitGraph.js +89 -0
- package/src/components/resources/ExplanationOfBenefitGraph/ExplanationOfBenefitGraph.stories.js +78 -0
- package/src/components/resources/ExplanationOfBenefitGraph/ExplanationOfBenefitGraph.test.js +51 -0
- package/src/components/resources/ExplanationOfBenefitGraph/index.js +3 -0
- package/src/components/resources/Goal/Goal.test.js +1 -1
- package/src/components/resources/Immunization/Immunization.js +125 -94
- package/src/components/resources/Immunization/Immunization.stories.js +23 -4
- package/src/components/resources/Immunization/Immunization.test.js +17 -12
- package/src/components/resources/List/List.test.js +3 -3
- package/src/components/resources/MedicationAdministration/MedicationAdministration.test.js +7 -7
- package/src/components/resources/MedicationDispense/MedicationDispense.test.js +2 -2
- package/src/components/resources/MedicationRequest/MedicationRequest.test.js +4 -4
- package/src/components/resources/Observation/Observation.js +72 -54
- package/src/components/resources/Observation/Observation.test.js +6 -18
- package/src/components/resources/Observation/ObservationGraph.js +159 -55
- package/src/components/resources/Observation/ObservationGraph.test.js +47 -26
- package/src/components/resources/Patient/Patient.js +79 -97
- package/src/components/resources/Patient/Patient.test.js +10 -10
- package/src/components/resources/Practitioner/Practitioner.js +80 -60
- package/src/components/resources/Practitioner/Practitioner.test.js +4 -4
- package/src/components/resources/Procedure/Procedure.js +99 -87
- package/src/components/resources/Procedure/Procedure.stories.js +8 -6
- package/src/components/resources/Procedure/Procedure.test.js +11 -8
- package/src/components/resources/Questionnaire/Questionnaire.test.js +3 -3
- package/src/components/resources/QuestionnaireResponse/QuestionnaireResponse.test.js +5 -5
- package/src/components/resources/ReferralRequest/ReferralRequest.test.js +2 -2
- package/src/components/resources/ResearchStudy/ResearchStudy.test.js +1 -1
- package/src/components/resources/ResourceCategory/ResourceCategory.js +56 -0
- package/src/components/resources/ResourceCategory/ResourceCategory.stories.js +29 -0
- package/src/components/resources/ResourceCategory/ResourceCategory.test.js +101 -0
- package/src/components/resources/ResourceCategory/index.js +3 -0
- package/src/components/ui/_header.scss +3 -0
- package/src/components/ui/bootstrap-reboot.min.css +2 -22
- package/src/components/ui/index.js +191 -29
- package/src/constants/badge-status.jsx +98 -0
- package/src/fixtures/dstu2/resources/condition/condition.svg +35 -0
- package/src/fixtures/dstu2/resources/immunization/immunization.svg +10 -0
- package/src/fixtures/example-icons.jsx +169 -0
- package/src/fixtures/r4/resources/explanationOfBenefit/c4bbExtendedDiagnosis.json +446 -0
- package/src/index.js +6 -1
- package/src/style.scss +176 -0
- package/src/utils/formatDate.js +21 -0
- package/src/utils/formatDate.test.js +22 -0
- package/src/utils/getBadgeColor.js +6 -0
- package/src/utils/getBadgeColor.test.js +14 -0
- package/src/utils/isUrl.js +9 -0
- package/src/utils/isUrl.test.js +12 -0
- package/src/utils.js +7 -0
- package/src/components/datatypes/HumanName/HumanName.css +0 -15
- package/src/components/datatypes/Reference/Reference.css +0 -8
- package/src/components/resources/Observation/ObservationGraph.css +0 -51
- package/src/components/resources/Patient/Patient.css +0 -19
- package/src/components/ui/index.css +0 -123
|
@@ -2,16 +2,17 @@ import React from 'react';
|
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
|
|
4
4
|
import _get from 'lodash/get';
|
|
5
|
-
import
|
|
5
|
+
import _isEmpty from 'lodash/isEmpty';
|
|
6
|
+
import Accordion from '../../containers/Accordion';
|
|
6
7
|
import Coding from '../../datatypes/Coding';
|
|
7
8
|
import Date from '../../datatypes/Date';
|
|
8
9
|
import ObservationGraph from './ObservationGraph';
|
|
9
10
|
import {
|
|
10
11
|
Root,
|
|
11
12
|
Header,
|
|
12
|
-
Title,
|
|
13
13
|
Badge,
|
|
14
14
|
BadgeSecondary,
|
|
15
|
+
ValueUnit,
|
|
15
16
|
Body,
|
|
16
17
|
Value,
|
|
17
18
|
} from '../../ui';
|
|
@@ -23,6 +24,7 @@ const Observation = props => {
|
|
|
23
24
|
const codeCodingDisplay = _get(fhirResource, 'code.coding.0.display');
|
|
24
25
|
const codeText = _get(fhirResource, 'code.text', '');
|
|
25
26
|
const valueQuantityValue = _get(fhirResource, 'valueQuantity.value', '');
|
|
27
|
+
const issued = _get(fhirResource, 'issued', '');
|
|
26
28
|
const valueQuantityUnit = _get(fhirResource, 'valueQuantity.unit', '');
|
|
27
29
|
const status = _get(fhirResource, 'status', '');
|
|
28
30
|
const valueCodeableConceptText = _get(
|
|
@@ -41,68 +43,84 @@ const Observation = props => {
|
|
|
41
43
|
|
|
42
44
|
let valueQuantityValueNumber = valueQuantityValue;
|
|
43
45
|
|
|
44
|
-
if (
|
|
45
|
-
_isFinite(Number(props.digitsToRoundForQuantity)) &&
|
|
46
|
-
valueQuantityValue !== '' &&
|
|
47
|
-
_isFinite(Number(valueQuantityValue))
|
|
48
|
-
) {
|
|
49
|
-
valueQuantityValueNumber = Number(valueQuantityValue).toFixed(
|
|
50
|
-
props.digitsToRoundForQuantity,
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const valueQuantityString = `${valueQuantityValueNumber}${valueQuantityUnit}`.trim();
|
|
55
46
|
const subject = _get(fhirResource, 'subject');
|
|
47
|
+
const tableData = [
|
|
48
|
+
{
|
|
49
|
+
label: 'Issued on',
|
|
50
|
+
testId: 'issuedOn',
|
|
51
|
+
data: effectiveDate && <Date fhirData={effectiveDate} isBlack />,
|
|
52
|
+
status: effectiveDate,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
label: 'Subject',
|
|
56
|
+
testId: 'subject',
|
|
57
|
+
data: subject && <Reference fhirData={subject} />,
|
|
58
|
+
status: subject,
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
label: 'Coding',
|
|
62
|
+
testId: 'coding',
|
|
63
|
+
data: valueCodeableConceptCoding.map((coding, i) => (
|
|
64
|
+
<Coding fhirData={coding} key={`value-coding-${i}`} />
|
|
65
|
+
)),
|
|
66
|
+
status: !_isEmpty(valueCodeableConceptCoding),
|
|
67
|
+
},
|
|
68
|
+
];
|
|
56
69
|
|
|
57
70
|
return (
|
|
58
71
|
<Root name="Observation">
|
|
59
|
-
<
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
72
|
+
<Accordion
|
|
73
|
+
headerContent={
|
|
74
|
+
<Header
|
|
75
|
+
resourceName={fhirResource.resourceType}
|
|
76
|
+
additionalContent={
|
|
77
|
+
issued && (
|
|
78
|
+
<Value label="Start date" data-testid="headerStartDate">
|
|
79
|
+
<Date fhirData={issued} isBlack />
|
|
80
|
+
</Value>
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
prefixBadge={
|
|
84
|
+
<ValueUnit
|
|
85
|
+
valueQty={valueQuantityValueNumber}
|
|
86
|
+
valueUnit={valueQuantityUnit}
|
|
87
|
+
/>
|
|
88
|
+
}
|
|
89
|
+
additionalBadge={
|
|
90
|
+
(valueCodeableConceptText ||
|
|
91
|
+
valueCodeableConceptCodingDisplay) && (
|
|
92
|
+
<BadgeSecondary data-testid="secondaryStatus">
|
|
93
|
+
{valueCodeableConceptText ||
|
|
94
|
+
valueCodeableConceptCodingDisplay}
|
|
95
|
+
</BadgeSecondary>
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
badges={status && <Badge data-testid="status">{status}</Badge>}
|
|
99
|
+
title={codeCodingDisplay || codeText}
|
|
100
|
+
rightAdditionalContent={
|
|
101
|
+
<ObservationGraph
|
|
102
|
+
valueQuantity={fhirResource.valueQuantity}
|
|
103
|
+
referenceRange={fhirResource.referenceRange}
|
|
104
|
+
small
|
|
105
|
+
/>
|
|
106
|
+
}
|
|
107
|
+
/>
|
|
108
|
+
}
|
|
109
|
+
bodyContent={
|
|
110
|
+
<Body tableData={tableData} reverseContent>
|
|
111
|
+
<ObservationGraph
|
|
112
|
+
valueQuantity={fhirResource.valueQuantity}
|
|
113
|
+
referenceRange={fhirResource.referenceRange}
|
|
114
|
+
/>
|
|
115
|
+
</Body>
|
|
116
|
+
}
|
|
117
|
+
/>
|
|
95
118
|
</Root>
|
|
96
119
|
);
|
|
97
120
|
};
|
|
98
121
|
|
|
99
122
|
Observation.propTypes = {
|
|
100
123
|
fhirResource: PropTypes.shape({}).isRequired,
|
|
101
|
-
digitsToRoundForQuantity: PropTypes.number,
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
Observation.defaultProps = {
|
|
105
|
-
digitsToRoundForQuantity: 2,
|
|
106
124
|
};
|
|
107
125
|
|
|
108
126
|
export default Observation;
|
|
@@ -62,14 +62,14 @@ describe('should render component correctly', () => {
|
|
|
62
62
|
);
|
|
63
63
|
expect(getByTestId('status').textContent).toEqual('final');
|
|
64
64
|
expect(getByTestId('secondaryStatus').textContent).toEqual('YES');
|
|
65
|
-
expect(getByTestId('issuedOn').textContent).toEqual('2016
|
|
65
|
+
expect(getByTestId('issuedOn').textContent).toEqual('5/18/2016');
|
|
66
66
|
expect(getByTestId('subject').textContent).toContain('Patient/infant');
|
|
67
67
|
expect(queryByText(/373066001/g)).not.toBeNull();
|
|
68
68
|
});
|
|
69
69
|
|
|
70
|
-
test('should
|
|
70
|
+
test('should display not rounded value', () => {
|
|
71
71
|
const resource = example1ObservationExcessR4;
|
|
72
|
-
resource.valueQuantity.value = 6.
|
|
72
|
+
resource.valueQuantity.value = 6.443;
|
|
73
73
|
|
|
74
74
|
const defaultProps = {
|
|
75
75
|
fhirResource: example1ObservationExcessR4,
|
|
@@ -77,20 +77,8 @@ describe('should render component correctly', () => {
|
|
|
77
77
|
const { getByTestId } = render(<Observation {...defaultProps} />);
|
|
78
78
|
|
|
79
79
|
expect(getByTestId('valueQuantity')).not.toBeNull();
|
|
80
|
-
expect(getByTestId('valueQuantity').textContent).toEqual('6.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
test('should round the quantity to specific value of digitsToRoundForQuantity props ', () => {
|
|
84
|
-
const resource = example1ObservationExcessR4;
|
|
85
|
-
resource.valueQuantity.value = 6.43534535434;
|
|
86
|
-
|
|
87
|
-
const defaultProps = {
|
|
88
|
-
fhirResource: example1ObservationExcessR4,
|
|
89
|
-
digitsToRoundForQuantity: 3,
|
|
90
|
-
};
|
|
91
|
-
const { getByTestId } = render(<Observation {...defaultProps} />);
|
|
92
|
-
|
|
93
|
-
expect(getByTestId('valueQuantity')).not.toBeNull();
|
|
94
|
-
expect(getByTestId('valueQuantity').textContent).toEqual('6.435mmol/l');
|
|
80
|
+
expect(getByTestId('valueQuantity').textContent).toEqual('6.443');
|
|
81
|
+
expect(getByTestId('valueQuantityUnit')).not.toBeNull();
|
|
82
|
+
expect(getByTestId('valueQuantityUnit').textContent).toEqual('mmol/l');
|
|
95
83
|
});
|
|
96
84
|
});
|
|
@@ -1,68 +1,172 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import _get from 'lodash/get';
|
|
3
3
|
import _isNumber from 'lodash/isNumber';
|
|
4
|
-
import './ObservationGraph.css';
|
|
5
4
|
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
const OBSERVATION_VALUE = 'observationValue';
|
|
6
|
+
const IN_RANGE = 'in range';
|
|
7
|
+
const OUT_OF_RANGE = 'out of range';
|
|
8
|
+
|
|
9
|
+
const rangeBaseClasses = `border border-1 border-light`;
|
|
10
|
+
|
|
11
|
+
const colorfulRange = ({
|
|
12
|
+
width = 1,
|
|
13
|
+
value = '',
|
|
14
|
+
rangeClasses = `bg-primary rounded-pill ${rangeBaseClasses}`,
|
|
15
|
+
}) => {
|
|
16
|
+
return {
|
|
17
|
+
width: width,
|
|
18
|
+
rangeClasses,
|
|
19
|
+
value,
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const rangeContent = (width, rangeClasses, value, small) => (
|
|
24
|
+
<div
|
|
25
|
+
className={`${rangeClasses} text-white text-center font-source`}
|
|
26
|
+
style={{ width: `${width}%`, lineHeight: '14px', minWidth: '8px' }}
|
|
27
|
+
key={`range${width}-${Math.random()}`}
|
|
28
|
+
data-testid={`rangeContent${value && value.replace(/ /g, '')}`}
|
|
29
|
+
>
|
|
30
|
+
{!small && value}
|
|
31
|
+
</div>
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const edgeRange = () => rangeContent(2, 'rounded-pill');
|
|
35
|
+
|
|
36
|
+
const observationValuePoint = (actualValue, unit, small) => {
|
|
37
|
+
return (
|
|
38
|
+
<div
|
|
39
|
+
className={`bg-dark ${rangeBaseClasses} rounded-circle ${
|
|
40
|
+
small ? 'mx-0' : 'mx-1'
|
|
41
|
+
}`}
|
|
42
|
+
style={{ width: small ? '9px' : '17px' }}
|
|
43
|
+
key={`ValuePoint-${Math.random()}`}
|
|
44
|
+
>
|
|
45
|
+
{!small && (
|
|
46
|
+
<span
|
|
47
|
+
className={`position-absolute top-0 translate-middle ps-2 ${
|
|
48
|
+
small ? 'pb-4 fs-1' : 'pb-5 fs-4'
|
|
49
|
+
} w-max-content`}
|
|
50
|
+
data-testid="valuePoint"
|
|
51
|
+
>
|
|
52
|
+
{`${actualValue} ${unit}`}
|
|
53
|
+
</span>
|
|
54
|
+
)}
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const valueIsInRange = (actualValue, tooLow, tooHigh, rangeInOneRow, unit) => {
|
|
60
|
+
const firstPart = { width: 1, value: '' };
|
|
61
|
+
const thirdPart = { width: 1, value: '' };
|
|
62
|
+
if (actualValue === tooLow) {
|
|
63
|
+
thirdPart.width = 94;
|
|
64
|
+
thirdPart.value = rangeInOneRow;
|
|
65
|
+
} else if (actualValue === tooHigh) {
|
|
66
|
+
firstPart.width = 94;
|
|
67
|
+
firstPart.value = rangeInOneRow;
|
|
68
|
+
} else {
|
|
69
|
+
const actualRangeWidthLeft = Math.round(
|
|
70
|
+
((actualValue - tooLow) * 100) / (tooHigh - tooLow),
|
|
71
|
+
);
|
|
72
|
+
const actualRangeWidthRight = 95 - 1 - actualRangeWidthLeft;
|
|
73
|
+
|
|
74
|
+
firstPart.width = actualRangeWidthLeft;
|
|
75
|
+
firstPart.value = `<${tooLow} ${unit}`;
|
|
76
|
+
thirdPart.width = actualRangeWidthRight;
|
|
77
|
+
thirdPart.value = `>${tooHigh} ${unit}`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return [
|
|
81
|
+
colorfulRange(firstPart),
|
|
82
|
+
OBSERVATION_VALUE,
|
|
83
|
+
colorfulRange(thirdPart),
|
|
84
|
+
];
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const valueIsOutOfRange = (actualValue, tooLow, tooHigh, rangeInOneRow) => {
|
|
88
|
+
if (actualValue < tooLow) {
|
|
89
|
+
const actualRangeWidth = Math.round(
|
|
90
|
+
((tooHigh - tooLow) * 100) / (tooHigh - actualValue),
|
|
91
|
+
);
|
|
92
|
+
return [
|
|
93
|
+
OBSERVATION_VALUE,
|
|
94
|
+
colorfulRange({
|
|
95
|
+
width: 95 - actualRangeWidth,
|
|
96
|
+
rangeClasses: 'rounded-pill',
|
|
97
|
+
}),
|
|
98
|
+
colorfulRange({ width: actualRangeWidth, value: rangeInOneRow }),
|
|
99
|
+
];
|
|
100
|
+
} else {
|
|
101
|
+
const actualRangeWidth = Math.round(
|
|
102
|
+
((tooHigh - tooLow) * 100) / (actualValue - tooLow),
|
|
103
|
+
);
|
|
104
|
+
return [
|
|
105
|
+
colorfulRange({ width: actualRangeWidth, value: rangeInOneRow }),
|
|
106
|
+
colorfulRange({
|
|
107
|
+
width: 95 - actualRangeWidth,
|
|
108
|
+
rangeClasses: 'rounded-pill',
|
|
109
|
+
}),
|
|
110
|
+
OBSERVATION_VALUE,
|
|
111
|
+
];
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const ObservationGraph = ({ referenceRange, valueQuantity, small }) => {
|
|
116
|
+
if (referenceRange && _isNumber(_get(valueQuantity, 'value'))) {
|
|
117
|
+
const tooLow = _get(referenceRange[0], 'low.value');
|
|
118
|
+
const tooHigh = _get(referenceRange[0], 'high.value');
|
|
119
|
+
|
|
120
|
+
const rangeInOneRow = `${tooLow} ${valueQuantity.unit} - ${tooHigh} ${valueQuantity.unit}`;
|
|
121
|
+
|
|
10
122
|
if (!tooLow || !tooHigh) return null;
|
|
11
123
|
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
124
|
+
const actualValue = valueQuantity.value;
|
|
125
|
+
const inRange = actualValue >= tooLow && actualValue <= tooHigh;
|
|
126
|
+
const graphRanges = inRange
|
|
127
|
+
? valueIsInRange(
|
|
128
|
+
actualValue,
|
|
129
|
+
tooLow,
|
|
130
|
+
tooHigh,
|
|
131
|
+
rangeInOneRow,
|
|
132
|
+
valueQuantity.unit,
|
|
133
|
+
)
|
|
134
|
+
: valueIsOutOfRange(actualValue, tooLow, tooHigh, rangeInOneRow);
|
|
135
|
+
|
|
136
|
+
const smallRangeText = inRange ? IN_RANGE : OUT_OF_RANGE;
|
|
22
137
|
|
|
23
138
|
return (
|
|
24
|
-
<div
|
|
139
|
+
<div
|
|
140
|
+
className={`position-relative ${
|
|
141
|
+
small ? 'w-110 graph-width-sm ms-2 ms-sm-0' : 'my-6'
|
|
142
|
+
}`}
|
|
143
|
+
>
|
|
144
|
+
{small && (
|
|
145
|
+
<span
|
|
146
|
+
className={`pb-1 fs-75 font-source d-inline-block`}
|
|
147
|
+
data-testid="smallRangeText"
|
|
148
|
+
>
|
|
149
|
+
{smallRangeText}
|
|
150
|
+
</span>
|
|
151
|
+
)}
|
|
25
152
|
<div
|
|
26
|
-
className="
|
|
27
|
-
style={{
|
|
153
|
+
className="d-flex overflow-hidden bg-gray-200 fs-75 rounded-pill"
|
|
154
|
+
style={{ height: small ? '0.5rem' : '1rem' }}
|
|
28
155
|
>
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
<strong data-testid="tooLow">{`< ${tooLow}`}</strong>
|
|
44
|
-
</small>
|
|
45
|
-
</div>
|
|
46
|
-
<div
|
|
47
|
-
className="fhir-resource__ObservationGraph__progress-bar--inside-range"
|
|
48
|
-
role="progressbar"
|
|
49
|
-
style={{ width: `${rangeBar}%` }}
|
|
50
|
-
aria-valuenow={actual}
|
|
51
|
-
aria-valuemin={tooLow}
|
|
52
|
-
aria-valuemax={tooHigh}
|
|
53
|
-
></div>
|
|
54
|
-
<div
|
|
55
|
-
className="fhir-resource__ObservationGraph__progress-value"
|
|
56
|
-
style={{ left: `${valueBar}%` }}
|
|
57
|
-
></div>
|
|
58
|
-
<div
|
|
59
|
-
className="fhir-resource__ObservationGraph__progress-bar--outside-range"
|
|
60
|
-
style={{ width: `${higherBar}%` }}
|
|
61
|
-
>
|
|
62
|
-
<small>
|
|
63
|
-
<strong data-testid="tooHigh">{`> ${tooHigh}`}</strong>
|
|
64
|
-
</small>
|
|
65
|
-
</div>
|
|
156
|
+
{edgeRange()}
|
|
157
|
+
{graphRanges.map(graphRange => {
|
|
158
|
+
if (graphRange === OBSERVATION_VALUE)
|
|
159
|
+
return observationValuePoint(
|
|
160
|
+
actualValue,
|
|
161
|
+
valueQuantity.unit,
|
|
162
|
+
small,
|
|
163
|
+
inRange,
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
const { rangeClasses, width, value } = graphRange;
|
|
167
|
+
return rangeContent(width, rangeClasses, value, small);
|
|
168
|
+
})}
|
|
169
|
+
{edgeRange()}
|
|
66
170
|
</div>
|
|
67
171
|
</div>
|
|
68
172
|
);
|
|
@@ -3,39 +3,41 @@ import { render } from '@testing-library/react';
|
|
|
3
3
|
import ObservationGraph from './ObservationGraph';
|
|
4
4
|
|
|
5
5
|
describe('ObservationGraph component', () => {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
{
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
value: 53,
|
|
21
|
-
},
|
|
22
|
-
text: '41 - 53 %',
|
|
6
|
+
const defaultProps = {
|
|
7
|
+
referenceRange: [
|
|
8
|
+
{
|
|
9
|
+
low: {
|
|
10
|
+
unit: '%',
|
|
11
|
+
code: '%',
|
|
12
|
+
system: 'http://unitsofmeasure.org',
|
|
13
|
+
value: 41,
|
|
14
|
+
},
|
|
15
|
+
high: {
|
|
16
|
+
unit: '%',
|
|
17
|
+
code: '%',
|
|
18
|
+
system: 'http://unitsofmeasure.org',
|
|
19
|
+
value: 53,
|
|
23
20
|
},
|
|
24
|
-
|
|
25
|
-
valueQuantity: {
|
|
26
|
-
unit: '%',
|
|
27
|
-
code: '%',
|
|
28
|
-
system: 'http://unitsofmeasure.org',
|
|
29
|
-
value: 41,
|
|
21
|
+
text: '41 - 53 %',
|
|
30
22
|
},
|
|
31
|
-
|
|
23
|
+
],
|
|
24
|
+
valueQuantity: {
|
|
25
|
+
unit: '%',
|
|
26
|
+
code: '%',
|
|
27
|
+
system: 'http://unitsofmeasure.org',
|
|
28
|
+
value: 41,
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
it('should render component correctly with proper props', () => {
|
|
32
32
|
const { container, getByTestId } = render(
|
|
33
33
|
<ObservationGraph {...defaultProps} />,
|
|
34
34
|
);
|
|
35
35
|
|
|
36
36
|
expect(container).not.toBeNull();
|
|
37
|
-
expect(getByTestId('
|
|
38
|
-
expect(getByTestId('
|
|
37
|
+
expect(getByTestId('valuePoint').textContent).toContain('41 %');
|
|
38
|
+
expect(getByTestId('rangeContent41%-53%').textContent).toContain(
|
|
39
|
+
'41 % - 53 %',
|
|
40
|
+
);
|
|
39
41
|
});
|
|
40
42
|
|
|
41
43
|
it('should return empty component without required props', () => {
|
|
@@ -45,4 +47,23 @@ describe('ObservationGraph component', () => {
|
|
|
45
47
|
expect(container).not.toBeNull();
|
|
46
48
|
expect(container.hasChildNodes()).toBeFalsy();
|
|
47
49
|
});
|
|
50
|
+
it('should render component correctly with small props and in range value', () => {
|
|
51
|
+
defaultProps.small = true;
|
|
52
|
+
const { container, getByTestId } = render(
|
|
53
|
+
<ObservationGraph {...defaultProps} />,
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
expect(container).not.toBeNull();
|
|
57
|
+
expect(getByTestId('smallRangeText').textContent).toContain('in range');
|
|
58
|
+
});
|
|
59
|
+
it('should render component correctly with small props and out of range value', () => {
|
|
60
|
+
defaultProps.small = true;
|
|
61
|
+
defaultProps.valueQuantity.value = 39;
|
|
62
|
+
const { container, getByTestId } = render(
|
|
63
|
+
<ObservationGraph {...defaultProps} />,
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
expect(container).not.toBeNull();
|
|
67
|
+
expect(getByTestId('smallRangeText').textContent).toContain('out of range');
|
|
68
|
+
});
|
|
48
69
|
});
|