@openmrs/esm-patient-tests-app 11.3.1-patch.9310 → 11.3.1-patch.9508

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 (170) hide show
  1. package/.turbo/turbo-build.log +19 -22
  2. package/dist/1119.js +1 -1
  3. package/dist/1197.js +1 -1
  4. package/dist/{6231.js → 1477.js} +1 -1
  5. package/dist/1477.js.map +1 -0
  6. package/dist/1638.js +1 -1
  7. package/dist/1638.js.map +1 -1
  8. package/dist/1935.js +1 -0
  9. package/dist/1935.js.map +1 -0
  10. package/dist/2146.js +1 -1
  11. package/dist/2690.js +1 -1
  12. package/dist/3099.js +1 -1
  13. package/dist/34.js +1 -0
  14. package/dist/34.js.map +1 -0
  15. package/dist/3509.js +1 -1
  16. package/dist/3509.js.map +1 -1
  17. package/dist/3584.js +1 -1
  18. package/dist/4055.js +1 -1
  19. package/dist/4132.js +1 -1
  20. package/dist/4300.js +1 -1
  21. package/dist/4335.js +1 -1
  22. package/dist/439.js +1 -0
  23. package/dist/4618.js +1 -1
  24. package/dist/4652.js +1 -1
  25. package/dist/4944.js +1 -1
  26. package/dist/5173.js +1 -1
  27. package/dist/5241.js +1 -1
  28. package/dist/5442.js +1 -1
  29. package/dist/5661.js +1 -1
  30. package/dist/5670.js +1 -1
  31. package/dist/6022.js +1 -1
  32. package/dist/6113.js +1 -0
  33. package/dist/6113.js.map +1 -0
  34. package/dist/6301.js +1 -1
  35. package/dist/6301.js.map +1 -1
  36. package/dist/6468.js +1 -1
  37. package/dist/6589.js +1 -0
  38. package/dist/6679.js +1 -1
  39. package/dist/6840.js +1 -1
  40. package/dist/6859.js +1 -1
  41. package/dist/7097.js +1 -1
  42. package/dist/7159.js +1 -1
  43. package/dist/7202.js +1 -0
  44. package/dist/7202.js.map +1 -0
  45. package/dist/723.js +1 -1
  46. package/dist/7617.js +1 -1
  47. package/dist/790.js +1 -1
  48. package/dist/790.js.map +1 -1
  49. package/dist/795.js +1 -1
  50. package/dist/8163.js +1 -1
  51. package/dist/8349.js +1 -1
  52. package/dist/8371.js +1 -0
  53. package/dist/8555.js +2 -0
  54. package/dist/8555.js.map +1 -0
  55. package/dist/8618.js +1 -1
  56. package/dist/890.js +1 -1
  57. package/dist/9214.js +1 -1
  58. package/dist/9538.js +1 -1
  59. package/dist/9569.js +1 -1
  60. package/dist/986.js +1 -1
  61. package/dist/9879.js +1 -1
  62. package/dist/9895.js +1 -1
  63. package/dist/9900.js +1 -1
  64. package/dist/9913.js +1 -1
  65. package/dist/main.js +1 -1
  66. package/dist/main.js.map +1 -1
  67. package/dist/openmrs-esm-patient-tests-app.js +1 -1
  68. package/dist/openmrs-esm-patient-tests-app.js.buildmanifest.json +302 -211
  69. package/dist/openmrs-esm-patient-tests-app.js.map +1 -1
  70. package/dist/routes.json +1 -1
  71. package/package.json +2 -2
  72. package/src/edit-test-results/modal/edit-lab-results.modal.tsx +2 -2
  73. package/src/index.ts +1 -1
  74. package/src/routes.json +4 -3
  75. package/src/test-orders/add-test-order/add-test-order.test.tsx +44 -21
  76. package/src/test-orders/add-test-order/add-test-order.workspace.tsx +152 -21
  77. package/src/test-orders/add-test-order/test-order-form.component.tsx +60 -68
  78. package/src/test-orders/add-test-order/test-order.ts +3 -3
  79. package/src/test-orders/add-test-order/test-type-search.component.tsx +60 -28
  80. package/src/test-orders/api.ts +2 -6
  81. package/src/test-orders/lab-order-basket-panel/lab-icon.component.tsx +27 -0
  82. package/src/test-orders/lab-order-basket-panel/lab-order-basket-item-tile.component.tsx +1 -1
  83. package/src/test-orders/lab-order-basket-panel/lab-order-basket-panel.extension.tsx +95 -30
  84. package/src/test-orders/lab-order-basket-panel/lab-order-basket-panel.scss +26 -11
  85. package/src/test-orders/lab-order-basket-panel/lab-order-basket-panel.test.tsx +12 -10
  86. package/src/test-results/filter/filter-context.test.tsx +556 -0
  87. package/src/test-results/filter/filter-context.tsx +1 -1
  88. package/src/test-results/filter/filter-reducer.test.ts +540 -0
  89. package/src/test-results/filter/filter-reducer.ts +1 -1
  90. package/src/test-results/filter/filter-set.component.tsx +75 -48
  91. package/src/test-results/filter/filter-set.test.tsx +694 -0
  92. package/src/test-results/filter/filter-types.ts +5 -1
  93. package/src/test-results/grouped-timeline/grid.component.tsx +4 -2
  94. package/src/test-results/grouped-timeline/grouped-timeline.component.tsx +20 -22
  95. package/src/test-results/grouped-timeline/grouped-timeline.test.tsx +3 -2
  96. package/src/test-results/grouped-timeline/reference-range-helpers.test.ts +37 -1
  97. package/src/test-results/grouped-timeline/reference-range-helpers.ts +50 -1
  98. package/src/test-results/grouped-timeline/timeline-data-group.component.tsx +4 -4
  99. package/src/test-results/grouped-timeline/useObstreeData.test.ts +471 -0
  100. package/src/test-results/grouped-timeline/useObstreeData.ts +37 -4
  101. package/src/test-results/individual-results-table/individual-results-table.component.tsx +2 -7
  102. package/src/test-results/individual-results-table-tablet/individual-results-table-tablet.component.tsx +3 -3
  103. package/src/test-results/individual-results-table-tablet/lab-set-panel.component.tsx +2 -5
  104. package/src/test-results/individual-results-table-tablet/usePanelData.tsx +40 -26
  105. package/src/test-results/loadPatientTestData/helpers.ts +29 -12
  106. package/src/test-results/loadPatientTestData/usePatientResultsData.ts +18 -7
  107. package/src/test-results/overview/external-overview.extension.tsx +1 -2
  108. package/src/test-results/print-modal/print-modal.extension.tsx +1 -1
  109. package/src/test-results/results-viewer/results-viewer.extension.tsx +8 -4
  110. package/src/test-results/tree-view/tree-view.component.tsx +18 -5
  111. package/src/test-results/tree-view/tree-view.test.tsx +119 -2
  112. package/src/test-results/trendline/trendline.component.tsx +88 -52
  113. package/src/test-results/ui-elements/{resetFiltersEmptyState → reset-filters-empty-state}/filter-empty-data-illustration.tsx +2 -2
  114. package/src/test-results/ui-elements/{resetFiltersEmptyState → reset-filters-empty-state}/filter-empty-state.component.tsx +5 -6
  115. package/src/types.ts +9 -0
  116. package/translations/am.json +3 -4
  117. package/translations/ar.json +3 -4
  118. package/translations/ar_SY.json +3 -4
  119. package/translations/bn.json +3 -4
  120. package/translations/cs.json +119 -0
  121. package/translations/de.json +3 -4
  122. package/translations/en.json +3 -4
  123. package/translations/en_US.json +3 -4
  124. package/translations/es.json +3 -4
  125. package/translations/es_MX.json +3 -4
  126. package/translations/fr.json +3 -4
  127. package/translations/he.json +3 -4
  128. package/translations/hi.json +3 -4
  129. package/translations/hi_IN.json +3 -4
  130. package/translations/id.json +3 -4
  131. package/translations/it.json +3 -4
  132. package/translations/ka.json +3 -4
  133. package/translations/km.json +3 -4
  134. package/translations/ku.json +3 -4
  135. package/translations/ky.json +3 -4
  136. package/translations/lg.json +3 -4
  137. package/translations/ne.json +3 -4
  138. package/translations/pl.json +3 -4
  139. package/translations/pt.json +3 -4
  140. package/translations/pt_BR.json +3 -4
  141. package/translations/qu.json +3 -4
  142. package/translations/ro_RO.json +3 -4
  143. package/translations/ru_RU.json +3 -4
  144. package/translations/si.json +3 -4
  145. package/translations/sq.json +119 -0
  146. package/translations/sw.json +3 -4
  147. package/translations/sw_KE.json +3 -4
  148. package/translations/tr.json +3 -4
  149. package/translations/tr_TR.json +3 -4
  150. package/translations/uk.json +3 -4
  151. package/translations/uz.json +3 -4
  152. package/translations/uz@Latn.json +3 -4
  153. package/translations/uz_UZ.json +3 -4
  154. package/translations/vi.json +3 -4
  155. package/translations/zh.json +3 -4
  156. package/translations/zh_CN.json +3 -4
  157. package/translations/zh_TW.json +119 -0
  158. package/dist/1479.js +0 -1
  159. package/dist/1479.js.map +0 -1
  160. package/dist/5348.js +0 -1
  161. package/dist/5348.js.map +0 -1
  162. package/dist/6231.js.map +0 -1
  163. package/dist/9540.js +0 -2
  164. package/dist/9540.js.map +0 -1
  165. package/dist/9838.js +0 -1
  166. package/dist/9838.js.map +0 -1
  167. package/src/test-orders/add-test-order/add-test-order.component.tsx +0 -125
  168. package/src/test-orders/add-test-order/exported-add-test-order.workspace.tsx +0 -30
  169. /package/dist/{9540.js.LICENSE.txt → 8555.js.LICENSE.txt} +0 -0
  170. /package/src/test-results/ui-elements/{resetFiltersEmptyState/index.scss → reset-filters-empty-state/filter-empty-state.scss} +0 -0
@@ -53,7 +53,7 @@ export type LowestNode = Pick<TreeNode, 'display' | 'flatName'>;
53
53
  export interface ReducerState {
54
54
  checkboxes: TreeCheckboxes;
55
55
  parents: TreeParents;
56
- roots: Array<LowestNode>;
56
+ roots: Array<TreeNode>;
57
57
  tests: TreeTests;
58
58
  lowestParents: Array<TreeNode>;
59
59
  }
@@ -147,3 +147,7 @@ export interface RowData extends TreeNode {
147
147
  | undefined
148
148
  >;
149
149
  }
150
+
151
+ export interface EmptyStateProps {
152
+ clearFilter(): void;
153
+ }
@@ -1,12 +1,14 @@
1
1
  import React from 'react';
2
2
  import styles from './grid.scss';
3
3
 
4
- export const Grid: React.FC<{
4
+ interface GridProps {
5
5
  children?: React.ReactNode;
6
6
  style: React.CSSProperties;
7
7
  padding?: boolean;
8
8
  dataColumns: number;
9
- }> = ({ dataColumns, style = {}, padding = false, ...props }) => {
9
+ }
10
+
11
+ export const Grid: React.FC<GridProps> = ({ dataColumns, style = {}, padding = false, ...props }) => {
10
12
  return (
11
13
  <div
12
14
  style={{
@@ -21,29 +21,27 @@ export const GroupedTimeline: React.FC<{ patientUuid: string }> = ({ patientUuid
21
21
 
22
22
  if (activeTests && timelineData && loaded && tableData) {
23
23
  return (
24
- <div>
25
- <div className={styles.timelineDataContainer}>
26
- {tableData.map((panel, index) => {
27
- // Filter rowData to only include tests that belong to this panel
28
- const panelTestNames = panel.entries.map((entry) => entry.flatName);
29
- const subRows = rowData?.filter((row: { flatName: string }) => panelTestNames.includes(row.flatName));
24
+ <div className={styles.timelineDataContainer}>
25
+ {tableData.map((panel, index) => {
26
+ // Filter rowData to only include tests that belong to this panel
27
+ const panelTestNames = panel.entries.map((entry) => entry.flatName);
28
+ const subRows = rowData?.filter((row: { flatName: string }) => panelTestNames.includes(row.flatName));
30
29
 
31
- return (
32
- subRows?.length > 0 && (
33
- <div key={index}>
34
- <TimelineDataGroup
35
- groupNumber={index + 1}
36
- parent={{ display: panel.key, flatName: panel.key }}
37
- patientUuid={patientUuid}
38
- setXScroll={setXScroll}
39
- subRows={subRows}
40
- xScroll={xScroll}
41
- />
42
- </div>
43
- )
44
- );
45
- })}
46
- </div>
30
+ return (
31
+ subRows?.length > 0 && (
32
+ <div key={index}>
33
+ <TimelineDataGroup
34
+ groupNumber={index + 1}
35
+ parent={{ display: panel.key, flatName: panel.key }}
36
+ patientUuid={patientUuid}
37
+ setXScroll={setXScroll}
38
+ subRows={subRows}
39
+ xScroll={xScroll}
40
+ />
41
+ </div>
42
+ )
43
+ );
44
+ })}
47
45
  </div>
48
46
  );
49
47
  }
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
- import { render, screen } from '@testing-library/react';
3
2
  import userEvent from '@testing-library/user-event';
3
+ import { render, screen } from '@testing-library/react';
4
4
  import { getByTextWithMarkup } from 'tools';
5
5
  import { showModal } from '@openmrs/esm-framework';
6
6
  import { mockGroupedResults } from '__mocks__';
@@ -62,7 +62,8 @@ describe('GroupedTimeline', () => {
62
62
  expect(screen.getByText('Nov 9')).toBeInTheDocument();
63
63
  expect(screen.getByText('01:39 AM')).toBeInTheDocument();
64
64
  expect(screen.getByText('Total bilirubin')).toBeInTheDocument();
65
- expect(screen.getByText('umol/L')).toBeInTheDocument();
65
+ // Units are now combined with range, so if there's no range, units won't be displayed separately
66
+ // Total bilirubin doesn't have a range in timelineData, so units are not displayed
66
67
  expect(screen.getByText('261.9')).toBeInTheDocument();
67
68
  expect(screen.getByText('21.5')).toBeInTheDocument();
68
69
  expect(screen.getByText('Serum glutamic-pyruvic transaminase')).toBeInTheDocument();
@@ -1,8 +1,9 @@
1
1
  import {
2
- selectReferenceRange,
2
+ formatRangeWithUnits,
3
3
  formatReferenceRange,
4
4
  getMostRecentObservationWithRange,
5
5
  rangeAlreadyHasUnits,
6
+ selectReferenceRange,
6
7
  type ReferenceRanges,
7
8
  } from './reference-range-helpers';
8
9
 
@@ -269,4 +270,39 @@ describe('Reference Range Helpers', () => {
269
270
  expect(rangeAlreadyHasUnits('0 – 50', '')).toBe(false);
270
271
  });
271
272
  });
273
+
274
+ describe('formatRangeWithUnits', () => {
275
+ it('returns "--" when range is undefined', () => {
276
+ expect(formatRangeWithUnits(undefined, 'U/L')).toBe('--');
277
+ });
278
+
279
+ it('returns "--" when range is empty string', () => {
280
+ expect(formatRangeWithUnits('', 'U/L')).toBe('--');
281
+ });
282
+
283
+ it('returns range as-is when range already includes units', () => {
284
+ expect(formatRangeWithUnits('0 – 50 U/L', 'U/L')).toBe('0 – 50 U/L');
285
+ });
286
+
287
+ it('appends units when range does not include units', () => {
288
+ expect(formatRangeWithUnits('0 – 50', 'U/L')).toBe('0 – 50 U/L');
289
+ });
290
+
291
+ it('returns range without units when units is undefined', () => {
292
+ expect(formatRangeWithUnits('0 – 50', undefined)).toBe('0 – 50');
293
+ });
294
+
295
+ it('returns range without units when units is empty', () => {
296
+ expect(formatRangeWithUnits('0 – 50', '')).toBe('0 – 50');
297
+ });
298
+
299
+ it('handles trimmed strings correctly', () => {
300
+ expect(formatRangeWithUnits(' 0 – 50 ', ' U/L ')).toBe('0 – 50 U/L');
301
+ });
302
+
303
+ it('handles range with units and different units parameter', () => {
304
+ // If range already has units, don't append different units
305
+ expect(formatRangeWithUnits('0 – 50 mg/dL', 'U/L')).toBe('0 – 50 mg/dL');
306
+ });
307
+ });
272
308
  });
@@ -1,5 +1,4 @@
1
1
  import { exist } from '../loadPatientTestData/helpers';
2
- import type { OBSERVATION_INTERPRETATION } from '@openmrs/esm-patient-common-lib';
3
2
 
4
3
  export interface ReferenceRanges {
5
4
  hiAbsolute?: number;
@@ -82,6 +81,56 @@ export function rangeAlreadyHasUnits(range: string | undefined, units: string |
82
81
  return trimmedRange.endsWith(trimmedUnits) || trimmedRange.endsWith(` ${trimmedUnits}`);
83
82
  }
84
83
 
84
+ /**
85
+ * Checks if a range string already contains any units (common unit patterns).
86
+ * This helps detect if a range already has units even if they differ from the parameter.
87
+ * @param range The formatted range string (e.g., "0 – 50 mg/dL")
88
+ * @returns true if the range appears to already contain units, false otherwise
89
+ */
90
+ function rangeHasAnyUnits(range: string): boolean {
91
+ if (!range) {
92
+ return false;
93
+ }
94
+
95
+ const trimmedRange = range.trim();
96
+ // Common unit patterns: ends with common unit abbreviations or contains unit-like patterns
97
+ // This is a heuristic to detect if units are already present
98
+ const unitPattern = /\s+[a-zA-Z\/%°]+$/;
99
+ return unitPattern.test(trimmedRange);
100
+ }
101
+
102
+ /**
103
+ * Formats a reference range with units for display, avoiding duplicate units.
104
+ * @param range The formatted range string (may or may not include units)
105
+ * @param units The units string to append if not already present
106
+ * @returns Formatted string with range and units (e.g., "0 – 50 U/L")
107
+ */
108
+ export function formatRangeWithUnits(range: string | undefined, units: string | undefined): string {
109
+ const trimmedRange = range?.trim() || '';
110
+ const trimmedUnits = units?.trim() || '';
111
+
112
+ // If range is empty, return '--' (even if units exist, we need a range to display)
113
+ if (!trimmedRange) {
114
+ return '--';
115
+ }
116
+
117
+ // Check if range already includes the specific units parameter
118
+ const hasSpecificUnits = rangeAlreadyHasUnits(trimmedRange, trimmedUnits);
119
+ if (hasSpecificUnits) {
120
+ return trimmedRange;
121
+ }
122
+
123
+ // Check if range already has any units (even if different from parameter)
124
+ // This prevents appending units when range already has different units (e.g., "0 – 50 mg/dL" with "U/L" parameter)
125
+ const hasAnyUnits = rangeHasAnyUnits(trimmedRange);
126
+ if (hasAnyUnits) {
127
+ return trimmedRange;
128
+ }
129
+
130
+ // Append units if not already present
131
+ return trimmedUnits ? `${trimmedRange} ${trimmedUnits}` : trimmedRange;
132
+ }
133
+
85
134
  /**
86
135
  * Finds the most recent observation that has reference range data.
87
136
  */
@@ -9,8 +9,8 @@ import type {
9
9
  NewRowStartCellProps,
10
10
  TimelineDataGroupProps,
11
11
  } from './grouped-timeline-types';
12
+ import { getMostRecentObservationWithRange, formatRangeWithUnits } from './reference-range-helpers';
12
13
  import FilterContext from '../filter/filter-context';
13
- import { getMostRecentObservationWithRange } from './reference-range-helpers';
14
14
  import styles from './grouped-timeline.scss';
15
15
 
16
16
  export const ShadowBox: React.FC = () => <div className={styles['shadow-box']} />;
@@ -131,6 +131,8 @@ const NewRowStartCell: React.FC<NewRowStartCellProps> = ({
131
131
  });
132
132
  }, [patientUuid, conceptUuid, title]);
133
133
 
134
+ const rangeUnitsDisplay = formatRangeWithUnits(range, units);
135
+
134
136
  return (
135
137
  <div
136
138
  className={styles.rowStartCell}
@@ -147,9 +149,7 @@ const NewRowStartCell: React.FC<NewRowStartCellProps> = ({
147
149
  <span className={styles.trendlineLink}>{title}</span>
148
150
  )}
149
151
  </span>
150
- <span className={styles.rangeUnits}>
151
- {range} {units}
152
- </span>
152
+ <span className={styles.rangeUnits}>{rangeUnitsDisplay}</span>
153
153
  </div>
154
154
  );
155
155
  };