fhir-react 0.2.1 → 0.3.0
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 +18 -0
- package/.storybook/config.js +9 -3
- package/.storybook/presets.js +1 -0
- package/.storybook/preview-head.html +4 -0
- package/README.md +86 -55
- 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/DrugTierDefinitionExtension.js +139 -0
- package/src/components/resources/List/Entries.js +66 -0
- package/src/components/resources/List/List.js +262 -0
- package/src/components/resources/List/List.stories.js +75 -0
- package/src/components/resources/List/List.test.js +95 -0
- package/src/components/resources/List/index.js +3 -0
- package/src/components/resources/List/utils.js +6 -0
- package/src/components/resources/MedicationAdministration/MedicationAdministration.test.js +7 -7
- package/src/components/resources/MedicationDispense/MedicationDispense.test.js +2 -2
- package/src/components/resources/MedicationKnowledge/MedicationKnowledge.js +217 -0
- package/src/components/resources/MedicationKnowledge/MedicationKnowledge.stories.js +78 -0
- package/src/components/resources/MedicationKnowledge/MedicationKnowledge.test.js +69 -0
- package/src/components/resources/MedicationKnowledge/index.js +3 -0
- package/src/components/resources/MedicationKnowledge/utils.js +8 -0
- 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/supportedFhirResourceList.js +4 -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/dstu2/resources/list/example1.json +49 -0
- package/src/fixtures/dstu2/resources/list/example2.json +116 -0
- package/src/fixtures/dstu2/resources/list/example3.json +380 -0
- package/src/fixtures/example-icons.jsx +169 -0
- package/src/fixtures/r4/resources/explanationOfBenefit/c4bbExtendedDiagnosis.json +446 -0
- package/src/fixtures/r4/resources/list/example1.json +45 -0
- package/src/fixtures/r4/resources/list/example2.json +282 -0
- package/src/fixtures/r4/resources/list/example3.json +298 -0
- package/src/fixtures/r4/resources/medicationKnowledge/example1.json +42 -0
- package/src/fixtures/r4/resources/medicationKnowledge/example2.json +59 -0
- package/src/fixtures/r4/resources/medicationKnowledge/example3.json +59 -0
- package/src/fixtures/r4/resources/medicationKnowledge/example4.json +59 -0
- package/src/fixtures/stu3/resources/list/example1.json +46 -0
- package/src/fixtures/stu3/resources/list/example2.json +298 -0
- package/src/fixtures/stu3/resources/list/example3.json +115 -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/build/bootstrap-reboot.min.css +0 -414
- package/build/index.js +0 -15
- package/build/style.css +0 -459
- 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
|
@@ -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
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import _get from 'lodash/get';
|
|
4
|
+
import _isEmpty from 'lodash/isEmpty';
|
|
4
5
|
import md5 from 'md5';
|
|
5
6
|
|
|
6
7
|
import HumanName from '../../datatypes/HumanName';
|
|
@@ -8,8 +9,8 @@ import Telecom from '../../datatypes/Telecom';
|
|
|
8
9
|
import Address from '../../datatypes/Address';
|
|
9
10
|
import Coding from '../../datatypes/Coding';
|
|
10
11
|
import Date from '../../datatypes/Date';
|
|
11
|
-
import
|
|
12
|
-
import '
|
|
12
|
+
import Accordion from '../../containers/Accordion';
|
|
13
|
+
import { Root, Header, MissingValue, Badge, Body } from '../../ui';
|
|
13
14
|
|
|
14
15
|
export function PatientContact(props) {
|
|
15
16
|
const { fhirData } = props;
|
|
@@ -20,7 +21,7 @@ export function PatientContact(props) {
|
|
|
20
21
|
<div>
|
|
21
22
|
<HumanName fhirData={name} />
|
|
22
23
|
{relationship && (
|
|
23
|
-
<small className="
|
|
24
|
+
<small className="text-bold">{` (${relationship})`}</small>
|
|
24
25
|
)}
|
|
25
26
|
</div>
|
|
26
27
|
);
|
|
@@ -30,7 +31,7 @@ export function getId(fhirResource) {
|
|
|
30
31
|
return _get(fhirResource, 'id');
|
|
31
32
|
}
|
|
32
33
|
export function getNames(fhirResource) {
|
|
33
|
-
return _get(fhirResource, 'name',
|
|
34
|
+
return _get(fhirResource, 'name.0', null);
|
|
34
35
|
}
|
|
35
36
|
export function getBirthDate(fhirResource) {
|
|
36
37
|
return _get(fhirResource, 'birthDate');
|
|
@@ -45,7 +46,7 @@ function Patient(props) {
|
|
|
45
46
|
const id = getId(fhirResource);
|
|
46
47
|
const idHash = md5(id || '');
|
|
47
48
|
const avatarSrc = `http://www.gravatar.com/avatar/${idHash}?s=50&r=any&default=identicon&forcedefault=1`;
|
|
48
|
-
const
|
|
49
|
+
const patientName = getNames(fhirResource);
|
|
49
50
|
const patientBirthDate = getBirthDate(fhirResource);
|
|
50
51
|
const patientGender = getGender(fhirResource);
|
|
51
52
|
const patientContact = _get(fhirResource, 'contact[0]');
|
|
@@ -57,7 +58,8 @@ function Patient(props) {
|
|
|
57
58
|
.filter(item => Boolean(_get(item, 'language.coding', null)))
|
|
58
59
|
.map(item => item.language.coding);
|
|
59
60
|
communicationLanguage = _get(communicationLanguage, '0', []);
|
|
60
|
-
const hasCommunicationLanguage = communicationLanguage
|
|
61
|
+
const hasCommunicationLanguage = !_isEmpty(communicationLanguage);
|
|
62
|
+
const hasPatientPhones = !_isEmpty(patientPhones);
|
|
61
63
|
const active = _get(fhirResource, 'active', false);
|
|
62
64
|
const activeStatus = active ? 'active' : 'inactive';
|
|
63
65
|
const deceasedBoolean = _get(fhirResource, 'deceasedBoolean', false);
|
|
@@ -68,100 +70,80 @@ function Patient(props) {
|
|
|
68
70
|
return <HumanName fhirData={patientName} primary={index === 0} />;
|
|
69
71
|
};
|
|
70
72
|
|
|
73
|
+
const tableData = [
|
|
74
|
+
{
|
|
75
|
+
label: 'Patient contact',
|
|
76
|
+
testId: 'patientContact',
|
|
77
|
+
data: patientContact && <PatientContact fhirData={patientContact} />,
|
|
78
|
+
status: patientContact,
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
label: 'Deceased',
|
|
82
|
+
testId: 'deceasedInfo',
|
|
83
|
+
data: deceasedDate ? <Date fhirData={deceasedDate} /> : 'yes',
|
|
84
|
+
status: !patientSimple && isDeceased,
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
label: 'Address',
|
|
88
|
+
testId: 'patientAddress',
|
|
89
|
+
data: patientAddress && <Address fhirData={patientAddress} />,
|
|
90
|
+
status: !patientSimple && patientAddress,
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
label: 'Telephone',
|
|
94
|
+
testId: 'patientPhones',
|
|
95
|
+
data: !hasPatientPhones ? (
|
|
96
|
+
<MissingValue />
|
|
97
|
+
) : (
|
|
98
|
+
patientPhones.map((telecom, index) => (
|
|
99
|
+
<Telecom key={index} fhirData={telecom} />
|
|
100
|
+
))
|
|
101
|
+
),
|
|
102
|
+
status: !patientSimple,
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
label: 'Communication - language',
|
|
106
|
+
testId: 'communicationLanguage',
|
|
107
|
+
data:
|
|
108
|
+
hasCommunicationLanguage &&
|
|
109
|
+
communicationLanguage.map((item, i) => (
|
|
110
|
+
<Coding key={`item-${i}`} fhirData={item} />
|
|
111
|
+
)),
|
|
112
|
+
status: !patientSimple && hasCommunicationLanguage,
|
|
113
|
+
},
|
|
114
|
+
];
|
|
115
|
+
|
|
71
116
|
return (
|
|
72
117
|
<Root name="Patient">
|
|
73
|
-
<
|
|
74
|
-
|
|
75
|
-
<
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
if (props.thorough === false && index !== 0) {
|
|
86
|
-
return null;
|
|
87
|
-
} else {
|
|
88
|
-
return (
|
|
89
|
-
<React.Fragment key={index}>
|
|
90
|
-
<span data-testid={`patientName-${index}`}>
|
|
91
|
-
{renderName
|
|
92
|
-
? renderName({
|
|
93
|
-
patientName,
|
|
94
|
-
defaultName,
|
|
95
|
-
fhirVersion,
|
|
96
|
-
id,
|
|
97
|
-
})
|
|
98
|
-
: defaultName(patientName, index)}
|
|
99
|
-
</span>
|
|
100
|
-
|
|
101
|
-
</React.Fragment>
|
|
102
|
-
);
|
|
103
|
-
}
|
|
104
|
-
})}
|
|
105
|
-
</div>
|
|
106
|
-
<div>
|
|
107
|
-
{active && (
|
|
108
|
-
<Badge data-testid="activeStatus">{activeStatus}</Badge>
|
|
109
|
-
)}
|
|
110
|
-
{patientBirthDate && (
|
|
111
|
-
<span className="fhir-resource__Patient__BirthDate-block">
|
|
112
|
-
<strong>
|
|
113
|
-
<span data-testid="patientGender">
|
|
114
|
-
{patientGender || 'unknown'}
|
|
115
|
-
</span>
|
|
116
|
-
{', '}
|
|
117
|
-
<span data-testid="patientBirthDate">
|
|
118
|
-
{patientBirthDate}
|
|
119
|
-
</span>
|
|
120
|
-
</strong>
|
|
121
|
-
<small> (DOB)</small>
|
|
118
|
+
<Accordion
|
|
119
|
+
headerContent={
|
|
120
|
+
<Header
|
|
121
|
+
resourceName={fhirResource.resourceType}
|
|
122
|
+
additionalContent={
|
|
123
|
+
patientBirthDate && (
|
|
124
|
+
<span className="text-gray-600">
|
|
125
|
+
<span data-testid="patientGender" className="text-capitalize">
|
|
126
|
+
{patientGender || 'unknown'}
|
|
127
|
+
</span>
|
|
128
|
+
{', '}
|
|
129
|
+
<span data-testid="patientBirthDate">{patientBirthDate}</span>
|
|
122
130
|
</span>
|
|
123
|
-
)
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
<Value label="Address" data-testid="patientAddress">
|
|
140
|
-
<Address fhirData={patientAddress} />
|
|
141
|
-
</Value>
|
|
142
|
-
)}
|
|
143
|
-
{patientPhones && (
|
|
144
|
-
<Value label="TELEPHONE" data-testid="patientPhones">
|
|
145
|
-
{patientPhones.map((telecom, index) => (
|
|
146
|
-
<div key={index}>
|
|
147
|
-
<Telecom fhirData={telecom} />
|
|
148
|
-
</div>
|
|
149
|
-
))}
|
|
150
|
-
{patientPhones.length === 0 && <MissingValue />}
|
|
151
|
-
</Value>
|
|
152
|
-
)}
|
|
153
|
-
{hasCommunicationLanguage && (
|
|
154
|
-
<Value
|
|
155
|
-
label="Communication - language"
|
|
156
|
-
data-testid="communicationLanguage"
|
|
157
|
-
>
|
|
158
|
-
{communicationLanguage.map((item, i) => (
|
|
159
|
-
<Coding key={`item-${i}`} fhirData={item} />
|
|
160
|
-
))}
|
|
161
|
-
</Value>
|
|
162
|
-
)}
|
|
163
|
-
</Body>
|
|
164
|
-
)}
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
icon={avatarSrc}
|
|
134
|
+
badges={
|
|
135
|
+
active && <Badge data-testid="activeStatus">{activeStatus}</Badge>
|
|
136
|
+
}
|
|
137
|
+
title={
|
|
138
|
+
renderName
|
|
139
|
+
? renderName({ patientName, defaultName, fhirVersion, id })
|
|
140
|
+
: defaultName(patientName, 0)
|
|
141
|
+
}
|
|
142
|
+
titleTestID="patientName"
|
|
143
|
+
/>
|
|
144
|
+
}
|
|
145
|
+
bodyContent={<Body tableData={tableData} />}
|
|
146
|
+
/>
|
|
165
147
|
</Root>
|
|
166
148
|
);
|
|
167
149
|
}
|
|
@@ -17,9 +17,9 @@ describe('should render component correctly', () => {
|
|
|
17
17
|
<Patient {...defaultProps} />,
|
|
18
18
|
);
|
|
19
19
|
|
|
20
|
-
expect(
|
|
21
|
-
|
|
22
|
-
)
|
|
20
|
+
expect(getByTestId('patientName').textContent.replace(/\s+/g, ' ')).toEqual(
|
|
21
|
+
'Jason Argonaut (usual)',
|
|
22
|
+
);
|
|
23
23
|
expect(getByTestId('patientGender').textContent).toEqual('male');
|
|
24
24
|
expect(getByTestId('patientBirthDate').textContent).toEqual('1985-08-01');
|
|
25
25
|
expect(getByTestId('patientAddress').textContent).toEqual(
|
|
@@ -40,9 +40,9 @@ describe('should render component correctly', () => {
|
|
|
40
40
|
<Patient {...defaultProps} />,
|
|
41
41
|
);
|
|
42
42
|
|
|
43
|
-
expect(
|
|
44
|
-
|
|
45
|
-
)
|
|
43
|
+
expect(getByTestId('patientName').textContent.replace(/\s+/g, ' ')).toEqual(
|
|
44
|
+
'John, X Doe (usual)',
|
|
45
|
+
);
|
|
46
46
|
expect(getByTestId('patientGender').textContent).toEqual('male');
|
|
47
47
|
expect(getByTestId('patientBirthDate').textContent).toEqual('2014-06-01');
|
|
48
48
|
expect(getByTestId('patientAddress').textContent).toEqual(' 05 99999 ');
|
|
@@ -67,9 +67,9 @@ describe('should render component correctly', () => {
|
|
|
67
67
|
<Patient {...defaultProps} />,
|
|
68
68
|
);
|
|
69
69
|
|
|
70
|
-
expect(
|
|
71
|
-
|
|
72
|
-
)
|
|
70
|
+
expect(getByTestId('patientName').textContent.replace(/\s+/g, ' ')).toEqual(
|
|
71
|
+
'Peter, James Chalmers (official)',
|
|
72
|
+
);
|
|
73
73
|
expect(getByTestId('patientGender').textContent).toEqual('male');
|
|
74
74
|
expect(getByTestId('patientBirthDate').textContent).toEqual('1974-12-25');
|
|
75
75
|
expect(getByTestId('patientAddress').textContent).toContain(
|
|
@@ -87,6 +87,6 @@ describe('should render component correctly', () => {
|
|
|
87
87
|
fhirResource: example3PatientR4,
|
|
88
88
|
};
|
|
89
89
|
const { getByTestId } = render(<Patient {...defaultProps} />);
|
|
90
|
-
expect(getByTestId('deceasedInfo').textContent).toEqual('2015
|
|
90
|
+
expect(getByTestId('deceasedInfo').textContent).toEqual('2/14/2015');
|
|
91
91
|
});
|
|
92
92
|
});
|