fhir-react 0.2.4 → 0.3.3

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.
Files changed (138) hide show
  1. package/.github/workflows/publish_npmjs.yml +20 -0
  2. package/.storybook/config.js +9 -3
  3. package/.storybook/presets.js +1 -0
  4. package/.storybook/preview-head.html +4 -0
  5. package/README.md +48 -4
  6. package/build/bootstrap-reboot.min.css +2 -22
  7. package/build/index.js +38 -3
  8. package/build/style.css +31 -459
  9. package/package.json +15 -5
  10. package/src/assets/common/chevron-right.svg +3 -0
  11. package/src/assets/containers/AllergyIntolerance/allergy-intolerance.svg +9 -0
  12. package/src/assets/containers/Appointment/appointment.svg +14 -0
  13. package/src/assets/containers/CarePlan/care-plan.svg +10 -0
  14. package/src/assets/containers/CareTeam/care-team.svg +10 -0
  15. package/src/assets/containers/Claim/claim.svg +6 -0
  16. package/src/assets/containers/ClaimResponse/claim-response.svg +7 -0
  17. package/src/assets/containers/Condition/condition.svg +11 -0
  18. package/src/assets/containers/Device/device.svg +8 -0
  19. package/src/assets/containers/DiagnosticReport/diagnostic-report.svg +14 -0
  20. package/src/assets/containers/DocumentReference/document-reference.svg +10 -0
  21. package/src/assets/containers/Encounter/encounter.svg +10 -0
  22. package/src/assets/containers/ExplanationOfBenefit/explanation-of-benefit.svg +3 -0
  23. package/src/assets/containers/FamilyMemberHistory/family-member-history.svg +7 -0
  24. package/src/assets/containers/Goal/goal.svg +11 -0
  25. package/src/assets/containers/Immunization/immunization.svg +7 -0
  26. package/src/assets/containers/List/list.svg +3 -0
  27. package/src/assets/containers/Location/location.svg +4 -0
  28. package/src/assets/containers/Medication/medication.svg +5 -0
  29. package/src/assets/containers/MedicationAdministration/medication-administration.svg +6 -0
  30. package/src/assets/containers/MedicationKnowledge/medication-knowledge.svg +11 -0
  31. package/src/assets/containers/MedicationStatement/medication-statement.svg +5 -0
  32. package/src/assets/containers/Observation/observation.svg +12 -0
  33. package/src/assets/containers/Practitioner/practitioner.svg +5 -0
  34. package/src/assets/containers/Procedure/procedure.svg +9 -0
  35. package/src/assets/containers/Questionnaire/questionnaire.svg +6 -0
  36. package/src/assets/containers/QuestionnaireResponse/questionnaire-response.svg +6 -0
  37. package/src/assets/containers/QustionnaireResponse/questionnaire-response.svg +6 -0
  38. package/src/assets/containers/ResearchStudy/research-study.svg +9 -0
  39. package/src/assets/containers/ResourceCategory/resource-placeholder.svg +3 -0
  40. package/src/components/containers/Accordion/Accordion.js +80 -0
  41. package/src/components/containers/Accordion/Accordion.stories.js +76 -0
  42. package/src/components/containers/Accordion/index.js +3 -0
  43. package/src/components/containers/ResourceContainer/ResourceContainer.css +0 -1
  44. package/src/components/containers/ResourceContainer/ResourceContainer.js +1 -1
  45. package/src/components/datatypes/AccountBalance/AccountBalance.js +33 -0
  46. package/src/components/datatypes/AccountBalance/index.js +3 -0
  47. package/src/components/datatypes/Annotation/Annotation.js +1 -1
  48. package/src/components/datatypes/Date/Date.js +14 -4
  49. package/src/components/datatypes/DatePeriod/DatePeriod.js +38 -0
  50. package/src/components/datatypes/DatePeriod/index.js +3 -0
  51. package/src/components/datatypes/HeaderIcon/HeaderIcon.js +31 -0
  52. package/src/components/datatypes/HeaderIcon/index.js +3 -0
  53. package/src/components/datatypes/HumanName/HumanName.js +6 -21
  54. package/src/components/datatypes/Reference/Reference.js +3 -6
  55. package/src/components/resources/AdverseEvent/AdverseEvent.test.js +2 -2
  56. package/src/components/resources/AllergyIntolerance/AllergyIntolerance.test.js +4 -4
  57. package/src/components/resources/Appointment/Appointment.js +91 -65
  58. package/src/components/resources/Appointment/Appointment.test.js +3 -3
  59. package/src/components/resources/Bundle/Bundle.js +2 -2
  60. package/src/components/resources/Bundle/Bundle.stories.js +78 -12
  61. package/src/components/resources/Bundle/Bundle.test.js +3 -0
  62. package/src/components/resources/CarePlan/CarePlan.test.js +4 -4
  63. package/src/components/resources/CareTeam/CareTeam.js +13 -14
  64. package/src/components/resources/CareTeam/CareTeam.test.js +4 -4
  65. package/src/components/resources/Claim/Claim.test.js +6 -6
  66. package/src/components/resources/ClaimResponse/ClaimResponse.test.js +6 -6
  67. package/src/components/resources/Condition/Condition.js +64 -47
  68. package/src/components/resources/Condition/Condition.stories.js +41 -8
  69. package/src/components/resources/Condition/Condition.test.js +20 -14
  70. package/src/components/resources/DiagnosticReport/DiagnosticReport.test.js +5 -7
  71. package/src/components/resources/DocumentReference/DocumentReference.js +1 -1
  72. package/src/components/resources/DocumentReference/DocumentReference.test.js +3 -3
  73. package/src/components/resources/Encounter/Encounter.js +66 -36
  74. package/src/components/resources/Encounter/EncounterParticipants.js +2 -2
  75. package/src/components/resources/ExplanationOfBenefit/CareTeam.js +2 -2
  76. package/src/components/resources/ExplanationOfBenefit/Diagnosis.js +15 -5
  77. package/src/components/resources/ExplanationOfBenefit/ExplanationOfBenefit.js +285 -203
  78. package/src/components/resources/ExplanationOfBenefit/ExplanationOfBenefit.test.js +86 -64
  79. package/src/components/resources/ExplanationOfBenefit/Items.js +2 -2
  80. package/src/components/resources/ExplanationOfBenefit/PriceLabel.js +20 -0
  81. package/src/components/resources/ExplanationOfBenefit/Related.js +3 -3
  82. package/src/components/resources/ExplanationOfBenefit/SupportingInfo.js +32 -6
  83. package/src/components/resources/ExplanationOfBenefit/TotalGraph.js +68 -0
  84. package/src/components/resources/ExplanationOfBenefitGraph/ExplanationOfBenefitGraph.js +89 -0
  85. package/src/components/resources/ExplanationOfBenefitGraph/ExplanationOfBenefitGraph.stories.js +78 -0
  86. package/src/components/resources/ExplanationOfBenefitGraph/ExplanationOfBenefitGraph.test.js +51 -0
  87. package/src/components/resources/ExplanationOfBenefitGraph/index.js +3 -0
  88. package/src/components/resources/Goal/Goal.test.js +1 -1
  89. package/src/components/resources/Immunization/Immunization.js +125 -94
  90. package/src/components/resources/Immunization/Immunization.stories.js +23 -4
  91. package/src/components/resources/Immunization/Immunization.test.js +17 -12
  92. package/src/components/resources/List/List.test.js +3 -3
  93. package/src/components/resources/MedicationAdministration/MedicationAdministration.test.js +7 -7
  94. package/src/components/resources/MedicationDispense/MedicationDispense.test.js +2 -2
  95. package/src/components/resources/MedicationRequest/MedicationRequest.test.js +4 -4
  96. package/src/components/resources/Observation/Observation.js +72 -54
  97. package/src/components/resources/Observation/Observation.test.js +6 -18
  98. package/src/components/resources/Observation/ObservationGraph.js +159 -55
  99. package/src/components/resources/Observation/ObservationGraph.test.js +47 -26
  100. package/src/components/resources/Patient/Patient.js +77 -87
  101. package/src/components/resources/Patient/Patient.test.js +1 -1
  102. package/src/components/resources/Practitioner/Practitioner.js +80 -60
  103. package/src/components/resources/Practitioner/Practitioner.test.js +4 -4
  104. package/src/components/resources/Procedure/Procedure.js +99 -87
  105. package/src/components/resources/Procedure/Procedure.stories.js +8 -6
  106. package/src/components/resources/Procedure/Procedure.test.js +11 -8
  107. package/src/components/resources/Questionnaire/Questionnaire.test.js +3 -3
  108. package/src/components/resources/QuestionnaireResponse/QuestionnaireResponse.test.js +5 -5
  109. package/src/components/resources/ReferralRequest/ReferralRequest.test.js +2 -2
  110. package/src/components/resources/ResearchStudy/ResearchStudy.test.js +1 -1
  111. package/src/components/resources/ResourceCategory/ResourceCategory.js +55 -0
  112. package/src/components/resources/ResourceCategory/ResourceCategory.stories.js +29 -0
  113. package/src/components/resources/ResourceCategory/ResourceCategory.test.js +101 -0
  114. package/src/components/resources/ResourceCategory/index.js +3 -0
  115. package/src/components/supportedFhirResourceList.js +2 -0
  116. package/src/components/ui/_header.scss +3 -0
  117. package/src/components/ui/bootstrap-reboot.min.css +2 -22
  118. package/src/components/ui/index.js +191 -29
  119. package/src/constants/badge-status.jsx +98 -0
  120. package/src/fixtures/dstu2/resources/condition/condition.svg +35 -0
  121. package/src/fixtures/dstu2/resources/immunization/immunization.svg +10 -0
  122. package/src/fixtures/example-icons.jsx +169 -0
  123. package/src/fixtures/r4/resources/explanationOfBenefit/c4bbExample.json +18 -2
  124. package/src/index.js +7 -1
  125. package/src/style.scss +176 -0
  126. package/src/utils/formatDate.js +21 -0
  127. package/src/utils/formatDate.test.js +22 -0
  128. package/src/utils/getBadgeColor.js +6 -0
  129. package/src/utils/getBadgeColor.test.js +14 -0
  130. package/src/utils/isUrl.js +9 -0
  131. package/src/utils/isUrl.test.js +12 -0
  132. package/src/utils.js +7 -0
  133. package/webpack.config.js +10 -1
  134. package/src/components/datatypes/HumanName/HumanName.css +0 -15
  135. package/src/components/datatypes/Reference/Reference.css +0 -8
  136. package/src/components/resources/Observation/ObservationGraph.css +0 -51
  137. package/src/components/resources/Patient/Patient.css +0 -19
  138. 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 _isFinite from 'lodash/isFinite';
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
- <Header>
60
- <Title>
61
- {codeCodingDisplay || codeText}
62
- {valueQuantityString && (
63
- <>
64
- &nbsp;
65
- <code data-testid="valueQuantity">{valueQuantityString}</code>
66
- </>
67
- )}
68
- </Title>
69
- {status && <Badge data-testid="status">{status}</Badge>}
70
- {(valueCodeableConceptText || valueCodeableConceptCodingDisplay) && (
71
- <BadgeSecondary data-testid="secondaryStatus">
72
- {valueCodeableConceptText || valueCodeableConceptCodingDisplay}
73
- </BadgeSecondary>
74
- )}
75
- </Header>
76
- <Body>
77
- <ObservationGraph
78
- valueQuantity={fhirResource.valueQuantity}
79
- referenceRange={fhirResource.referenceRange}
80
- />
81
- {effectiveDate && (
82
- <Value label="Issued on" data-testid="issuedOn">
83
- <Date fhirData={effectiveDate} />
84
- </Value>
85
- )}
86
- {subject && (
87
- <Value label="Subject" data-testid="subject">
88
- <Reference fhirData={subject} />
89
- </Value>
90
- )}
91
- {valueCodeableConceptCoding.map((coding, i) => (
92
- <Coding fhirData={coding} key={`value-coding-${i}`} />
93
- ))}
94
- </Body>
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-05-18');
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 round the quantity to default value of digitsToRoundForQuantity props ', () => {
70
+ test('should display not rounded value', () => {
71
71
  const resource = example1ObservationExcessR4;
72
- resource.valueQuantity.value = 6.43534535434;
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.44mmol/l');
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 ObservationGraph = props => {
7
- if (props.referenceRange && _isNumber(_get(props, 'valueQuantity.value'))) {
8
- const tooLow = _get(props, 'referenceRange[0].low.value');
9
- const tooHigh = _get(props, 'referenceRange[0].high.value');
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 actual = props.valueQuantity.value;
13
- const maxNum = Math.max(tooHigh, actual);
14
- const minNum = Math.min(tooLow, actual);
15
- const startAt = (maxNum - minNum) * -0.5 + minNum;
16
- const endAt = (maxNum - minNum) * 0.5 + maxNum;
17
- const zeroShift = 0 - startAt;
18
- const lowBar = Math.round(((zeroShift + tooLow) / endAt) * 100);
19
- const rangeBar = Math.round(((zeroShift + tooHigh) / endAt) * 100) - lowBar;
20
- const valueBar = Math.round(((zeroShift + actual) / endAt) * 100) + 3;
21
- const higherBar = 100 - rangeBar - lowBar;
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 className="fhir-resource__ObservationGraph">
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="fhir-resource__ObservationGraph__value-wrapper"
27
- style={{ left: `${valueBar}%` }}
153
+ className="d-flex overflow-hidden bg-gray-200 fs-75 rounded-pill"
154
+ style={{ height: small ? '0.5rem' : '1rem' }}
28
155
  >
29
- <code className="fhir-resource__ObservationGraph__value-actual">
30
- {actual}
31
- </code>
32
- &nbsp;
33
- <span className="fhir-resource__ObservationGraph__value-unit">
34
- {props.valueQuantity.unit}
35
- </span>
36
- </div>
37
- <div className="fhir-resource__ObservationGraph__progress">
38
- <div
39
- className="fhir-resource__ObservationGraph__progress-bar--outside-range"
40
- style={{ width: `${lowBar}%` }}
41
- >
42
- <small>
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
- it('should render component correctly with proper props', () => {
7
- const defaultProps = {
8
- referenceRange: [
9
- {
10
- low: {
11
- unit: '%',
12
- code: '%',
13
- system: 'http://unitsofmeasure.org',
14
- value: 41,
15
- },
16
- high: {
17
- unit: '%',
18
- code: '%',
19
- system: 'http://unitsofmeasure.org',
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('tooLow').textContent).toContain('41');
38
- expect(getByTestId('tooHigh').textContent).toContain('53');
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
  });