@aehrc/smart-forms-renderer 0.23.0 → 0.23.2

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 (53) hide show
  1. package/lib/components/FormComponents/GroupItem/GroupItem.styles.d.ts +1 -1
  2. package/lib/components/FormComponents/ItemParts/ItemLabelText.js +7 -1
  3. package/lib/components/FormComponents/ItemParts/ItemLabelText.js.map +1 -1
  4. package/lib/components/FormComponents/SingleItem/SingleItem.js +3 -2
  5. package/lib/components/FormComponents/SingleItem/SingleItem.js.map +1 -1
  6. package/lib/hooks/useDecimalCalculatedExpression.js +6 -2
  7. package/lib/hooks/useDecimalCalculatedExpression.js.map +1 -1
  8. package/lib/hooks/useDisplayCalculatedExpression.d.ts +3 -0
  9. package/lib/hooks/useDisplayCalculatedExpression.js +33 -0
  10. package/lib/hooks/useDisplayCalculatedExpression.js.map +1 -0
  11. package/lib/hooks/useIntegerCalculatedExpression.js +6 -2
  12. package/lib/hooks/useIntegerCalculatedExpression.js.map +1 -1
  13. package/lib/hooks/useStringCalculatedExpression.js +13 -5
  14. package/lib/hooks/useStringCalculatedExpression.js.map +1 -1
  15. package/lib/hooks/useValueSetCodings.js +1 -1
  16. package/lib/hooks/useValueSetCodings.js.map +1 -1
  17. package/lib/interfaces/calculatedExpression.interface.d.ts +1 -0
  18. package/lib/interfaces/questionnaireStore.interface.d.ts +1 -1
  19. package/lib/stores/questionnaireStore.d.ts +2 -2
  20. package/lib/utils/calculatedExpression.d.ts +5 -5
  21. package/lib/utils/calculatedExpression.js +43 -29
  22. package/lib/utils/calculatedExpression.js.map +1 -1
  23. package/lib/utils/fhirpath.d.ts +2 -2
  24. package/lib/utils/getExpressionsFromItem.d.ts +20 -0
  25. package/lib/utils/getExpressionsFromItem.js +90 -0
  26. package/lib/utils/getExpressionsFromItem.js.map +1 -0
  27. package/lib/utils/initialise.d.ts +2 -2
  28. package/lib/utils/itemControl.d.ts +1 -7
  29. package/lib/utils/itemControl.js +0 -20
  30. package/lib/utils/itemControl.js.map +1 -1
  31. package/lib/utils/questionnaireStoreUtils/extractOtherExtensions.d.ts +2 -14
  32. package/lib/utils/questionnaireStoreUtils/extractOtherExtensions.js +6 -46
  33. package/lib/utils/questionnaireStoreUtils/extractOtherExtensions.js.map +1 -1
  34. package/lib/utils/removeEmptyAnswers.js +3 -0
  35. package/lib/utils/removeEmptyAnswers.js.map +1 -1
  36. package/package.json +1 -1
  37. package/src/components/FormComponents/ItemParts/ItemLabelText.tsx +12 -1
  38. package/src/components/FormComponents/SingleItem/SingleItem.tsx +3 -2
  39. package/src/hooks/useDecimalCalculatedExpression.ts +8 -2
  40. package/src/hooks/useDisplayCalculatedExpression.ts +40 -0
  41. package/src/hooks/useIntegerCalculatedExpression.ts +8 -2
  42. package/src/hooks/useStringCalculatedExpression.ts +19 -5
  43. package/src/hooks/useValueSetCodings.ts +1 -1
  44. package/src/interfaces/calculatedExpression.interface.ts +1 -0
  45. package/src/interfaces/questionnaireStore.interface.ts +1 -1
  46. package/src/stores/questionnaireStore.ts +1 -1
  47. package/src/utils/calculatedExpression.ts +76 -60
  48. package/src/utils/fhirpath.ts +2 -2
  49. package/src/utils/getExpressionsFromItem.ts +96 -0
  50. package/src/utils/initialise.ts +2 -2
  51. package/src/utils/itemControl.ts +1 -21
  52. package/src/utils/questionnaireStoreUtils/extractOtherExtensions.ts +13 -50
  53. package/src/utils/removeEmptyAnswers.ts +4 -0
@@ -37,7 +37,7 @@ import dayjs from 'dayjs';
37
37
 
38
38
  interface EvaluateInitialCalculatedExpressionsParams {
39
39
  initialResponse: QuestionnaireResponse;
40
- calculatedExpressions: Record<string, CalculatedExpression>;
40
+ calculatedExpressions: Record<string, CalculatedExpression[]>;
41
41
  variablesFhirPath: Record<string, Expression[]>;
42
42
  existingFhirPathContext: Record<string, any>;
43
43
  }
@@ -45,7 +45,7 @@ interface EvaluateInitialCalculatedExpressionsParams {
45
45
  export function evaluateInitialCalculatedExpressions(
46
46
  params: EvaluateInitialCalculatedExpressionsParams
47
47
  ): {
48
- initialCalculatedExpressions: Record<string, CalculatedExpression>;
48
+ initialCalculatedExpressions: Record<string, CalculatedExpression[]>;
49
49
  updatedFhirPathContext: Record<string, any>;
50
50
  } {
51
51
  const { initialResponse, calculatedExpressions, variablesFhirPath, existingFhirPathContext } =
@@ -62,7 +62,7 @@ export function evaluateInitialCalculatedExpressions(
62
62
  };
63
63
  }
64
64
 
65
- const initialCalculatedExpressions: Record<string, CalculatedExpression> = {
65
+ const initialCalculatedExpressions: Record<string, CalculatedExpression[]> = {
66
66
  ...calculatedExpressions
67
67
  };
68
68
  const updatedFhirPathContext = createFhirPathContext(
@@ -72,23 +72,26 @@ export function evaluateInitialCalculatedExpressions(
72
72
  );
73
73
 
74
74
  for (const linkId in initialCalculatedExpressions) {
75
- try {
76
- const result = fhirpath.evaluate(
77
- initialResponse,
78
- calculatedExpressions[linkId].expression,
79
- updatedFhirPathContext,
80
- fhirpath_r4_model
81
- );
75
+ const itemCalcExpressions = calculatedExpressions[linkId];
76
+
77
+ for (const calcExpression of itemCalcExpressions) {
78
+ try {
79
+ const result = fhirpath.evaluate(
80
+ {},
81
+ calcExpression.expression,
82
+ updatedFhirPathContext,
83
+ fhirpath_r4_model
84
+ );
82
85
 
83
- if (!_isEqual(calculatedExpressions[linkId].value, result[0])) {
84
- initialCalculatedExpressions[linkId].value = result[0];
86
+ if (!_isEqual(calcExpression.value, result[0])) {
87
+ calcExpression.value = result[0];
88
+ }
89
+ } catch (e) {
90
+ console.warn(e.message, `LinkId: ${linkId}\nExpression: ${calcExpression.expression}`);
85
91
  }
86
- } catch (e) {
87
- console.warn(
88
- e.message,
89
- `LinkId: ${linkId}\nExpression: ${calculatedExpressions[linkId].expression}`
90
- );
91
92
  }
93
+
94
+ initialCalculatedExpressions[linkId] = itemCalcExpressions;
92
95
  }
93
96
 
94
97
  return {
@@ -99,37 +102,40 @@ export function evaluateInitialCalculatedExpressions(
99
102
 
100
103
  export function evaluateCalculatedExpressions(
101
104
  fhirPathContext: Record<string, any>,
102
- calculatedExpressions: Record<string, CalculatedExpression>
105
+ calculatedExpressions: Record<string, CalculatedExpression[]>
103
106
  ): {
104
107
  calculatedExpsIsUpdated: boolean;
105
- updatedCalculatedExpressions: Record<string, CalculatedExpression>;
108
+ updatedCalculatedExpressions: Record<string, CalculatedExpression[]>;
106
109
  } {
107
- const updatedCalculatedExpressions: Record<string, CalculatedExpression> = {
110
+ const updatedCalculatedExpressions: Record<string, CalculatedExpression[]> = {
108
111
  ...calculatedExpressions
109
112
  };
110
113
 
111
114
  let isUpdated = false;
112
115
  for (const linkId in calculatedExpressions) {
113
- try {
114
- const result = fhirpath.evaluate(
115
- '',
116
- calculatedExpressions[linkId].expression,
117
- fhirPathContext,
118
- fhirpath_r4_model
119
- );
116
+ const itemCalcExpressions = calculatedExpressions[linkId];
117
+
118
+ for (const calcExpression of itemCalcExpressions) {
119
+ try {
120
+ const result = fhirpath.evaluate(
121
+ {},
122
+ calcExpression.expression,
123
+ fhirPathContext,
124
+ fhirpath_r4_model
125
+ );
120
126
 
121
- if (result.length > 0) {
122
- if (!_isEqual(calculatedExpressions[linkId].value, result[0])) {
123
- isUpdated = true;
124
- updatedCalculatedExpressions[linkId].value = result[0];
127
+ if (result.length > 0) {
128
+ if (!_isEqual(calcExpression.value, result[0])) {
129
+ isUpdated = true;
130
+ calcExpression.value = result[0];
131
+ }
125
132
  }
133
+ } catch (e) {
134
+ console.warn(e.message, `LinkId: ${linkId}\nExpression: ${calcExpression.expression}`);
126
135
  }
127
- } catch (e) {
128
- console.warn(
129
- e.message,
130
- `LinkId: ${linkId}\nExpression: ${calculatedExpressions[linkId].expression}`
131
- );
132
136
  }
137
+
138
+ updatedCalculatedExpressions[linkId] = itemCalcExpressions;
133
139
  }
134
140
 
135
141
  return {
@@ -141,17 +147,8 @@ export function evaluateCalculatedExpressions(
141
147
  export function initialiseCalculatedExpressionValues(
142
148
  questionnaire: Questionnaire,
143
149
  populatedResponse: QuestionnaireResponse,
144
- calculatedExpressions: Record<string, CalculatedExpression>
150
+ calculatedExpressions: Record<string, CalculatedExpression[]>
145
151
  ): QuestionnaireResponse {
146
- const calculatedExpressionsWithValues = Object.keys(calculatedExpressions)
147
- .filter((key) => calculatedExpressions[key].value !== undefined)
148
- .reduce(
149
- (mapping: Record<string, CalculatedExpression>, key: string) => (
150
- (mapping[key] = calculatedExpressions[key]), mapping
151
- ),
152
- {}
153
- );
154
-
155
152
  if (
156
153
  !questionnaire.item ||
157
154
  questionnaire.item.length === 0 ||
@@ -161,6 +158,19 @@ export function initialiseCalculatedExpressionValues(
161
158
  return populatedResponse;
162
159
  }
163
160
 
161
+ // Filter calculated expressions, only preserve key-value pairs with values
162
+ const calculatedExpressionsWithValues: Record<string, CalculatedExpression[]> = {};
163
+ for (const linkId in calculatedExpressions) {
164
+ const itemCalcExpressionsWithValues = calculatedExpressions[linkId].filter(
165
+ (calcExpression) => calcExpression.value !== undefined
166
+ );
167
+
168
+ if (itemCalcExpressionsWithValues.length > 0) {
169
+ calculatedExpressionsWithValues[linkId] = itemCalcExpressionsWithValues;
170
+ }
171
+ }
172
+
173
+ // Populate calculated expression values into QR
164
174
  const topLevelQrItems: QuestionnaireResponseItem[] = [];
165
175
  for (const [index, topLevelQItem] of questionnaire.item.entries()) {
166
176
  const populatedTopLevelQrItem = populatedResponse.item[index] ?? {
@@ -193,7 +203,7 @@ export function initialiseCalculatedExpressionValues(
193
203
  function initialiseItemCalculatedExpressionValueRecursive(
194
204
  qItem: QuestionnaireItem,
195
205
  qrItem: QuestionnaireResponseItem | undefined,
196
- calculatedExpressions: Record<string, CalculatedExpression>
206
+ calculatedExpressions: Record<string, CalculatedExpression[]>
197
207
  ): QuestionnaireResponseItem[] | QuestionnaireResponseItem | null {
198
208
  const childQItems = qItem.item;
199
209
  if (childQItems && childQItems.length > 0) {
@@ -245,16 +255,27 @@ function initialiseItemCalculatedExpressionValueRecursive(
245
255
  return constructSingleItem(qItem, calculatedExpressions);
246
256
  }
247
257
 
258
+ function getCalculatedExpressionAnswer(
259
+ qItem: QuestionnaireItem,
260
+ calculatedExpressions: Record<string, CalculatedExpression[]>
261
+ ): QuestionnaireResponseItemAnswer | undefined {
262
+ const calcExpressionFromItem = calculatedExpressions[qItem.linkId]?.find(
263
+ (calcExpression) => calcExpression.from === 'item'
264
+ );
265
+
266
+ if (calcExpressionFromItem && calcExpressionFromItem.value) {
267
+ return parseValueToAnswer(qItem, calcExpressionFromItem.value);
268
+ }
269
+
270
+ return;
271
+ }
272
+
248
273
  function constructGroupItem(
249
274
  qItem: QuestionnaireItem,
250
275
  qrItem: QuestionnaireResponseItem | undefined,
251
- calculatedExpressions: Record<string, CalculatedExpression>
276
+ calculatedExpressions: Record<string, CalculatedExpression[]>
252
277
  ): QuestionnaireResponseItem | null {
253
- const itemCalculatedExpression = calculatedExpressions[qItem.linkId];
254
- let calculatedExpressionAnswer: QuestionnaireResponseItemAnswer | undefined;
255
- if (itemCalculatedExpression && itemCalculatedExpression.value) {
256
- calculatedExpressionAnswer = parseValueToAnswer(qItem, itemCalculatedExpression.value);
257
- }
278
+ const calculatedExpressionAnswer = getCalculatedExpressionAnswer(qItem, calculatedExpressions);
258
279
 
259
280
  // If group item has an existing answer, do not overwrite it with calculated expression value
260
281
  if (qrItem?.answer && qrItem?.answer.length > 0) {
@@ -281,14 +302,9 @@ function constructGroupItem(
281
302
 
282
303
  function constructSingleItem(
283
304
  qItem: QuestionnaireItem,
284
- calculatedExpressions: Record<string, CalculatedExpression>
305
+ calculatedExpressions: Record<string, CalculatedExpression[]>
285
306
  ): QuestionnaireResponseItem | null {
286
- const itemCalculatedExpression = calculatedExpressions[qItem.linkId];
287
- let calculatedExpressionAnswer: QuestionnaireResponseItemAnswer | undefined;
288
- if (itemCalculatedExpression && itemCalculatedExpression.value) {
289
- calculatedExpressionAnswer = parseValueToAnswer(qItem, itemCalculatedExpression.value);
290
- }
291
-
307
+ const calculatedExpressionAnswer = getCalculatedExpressionAnswer(qItem, calculatedExpressions);
292
308
  if (!calculatedExpressionAnswer) {
293
309
  return null;
294
310
  }
@@ -25,7 +25,7 @@ import { evaluateCalculatedExpressions } from './calculatedExpression';
25
25
 
26
26
  interface EvaluateUpdatedExpressionsParams {
27
27
  updatedResponse: QuestionnaireResponse;
28
- calculatedExpressions: Record<string, CalculatedExpression>;
28
+ calculatedExpressions: Record<string, CalculatedExpression[]>;
29
29
  enableWhenExpressions: EnableWhenExpressions;
30
30
  variablesFhirPath: Record<string, Expression[]>;
31
31
  existingFhirPathContext: Record<string, any>;
@@ -34,7 +34,7 @@ interface EvaluateUpdatedExpressionsParams {
34
34
  export function evaluateUpdatedExpressions(params: EvaluateUpdatedExpressionsParams): {
35
35
  isUpdated: boolean;
36
36
  updatedEnableWhenExpressions: EnableWhenExpressions;
37
- updatedCalculatedExpressions: Record<string, CalculatedExpression>;
37
+ updatedCalculatedExpressions: Record<string, CalculatedExpression[]>;
38
38
  updatedFhirPathContext: Record<string, any>;
39
39
  } {
40
40
  const {
@@ -0,0 +1,96 @@
1
+ /*
2
+ * Copyright 2024 Commonwealth Scientific and Industrial Research
3
+ * Organisation (CSIRO) ABN 41 687 119 230.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ import type { Expression, Extension, QuestionnaireItem } from 'fhir/r4';
19
+ import type { CalculatedExpression } from '../interfaces/calculatedExpression.interface';
20
+
21
+ /**
22
+ * Get enableWhenExpression.valueExpression if its present in item
23
+ *
24
+ * @author Sean Fong
25
+ */
26
+ export function getEnableWhenExpression(qItem: QuestionnaireItem): Expression | null {
27
+ const enableWhenExpression = qItem.extension?.find(
28
+ (extension: Extension) =>
29
+ extension.url ===
30
+ 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-enableWhenExpression' &&
31
+ extension.valueExpression?.language === 'text/fhirpath'
32
+ );
33
+
34
+ if (enableWhenExpression?.valueExpression) {
35
+ return enableWhenExpression.valueExpression;
36
+ }
37
+ return null;
38
+ }
39
+
40
+ /**
41
+ * Get calculatedExpression.valueExpression if its present in item or item._text
42
+ *
43
+ * @author Sean Fong
44
+ */
45
+ export function getCalculatedExpressions(qItem: QuestionnaireItem): CalculatedExpression[] {
46
+ const calculatedExpressionsInItem = findCalculatedExpressionsInExtensions(qItem.extension ?? [])
47
+ .map(
48
+ (calculatedExpression): CalculatedExpression => ({
49
+ expression: calculatedExpression.valueExpression?.expression ?? '',
50
+ from: 'item'
51
+ })
52
+ )
53
+ .filter((calculatedExpression) => calculatedExpression.expression !== '');
54
+
55
+ const calculatedExpressionsInText = findCalculatedExpressionsInExtensions(
56
+ qItem._text?.extension ?? []
57
+ )
58
+ .map(
59
+ (calculatedExpression): CalculatedExpression => ({
60
+ expression: calculatedExpression.valueExpression?.expression ?? '',
61
+ from: 'item._text'
62
+ })
63
+ )
64
+ .filter((calculatedExpression) => calculatedExpression.expression !== '');
65
+
66
+ return [...calculatedExpressionsInItem, ...calculatedExpressionsInText];
67
+ }
68
+
69
+ function findCalculatedExpressionsInExtensions(extensions: Extension[]): Extension[] {
70
+ return extensions.filter(
71
+ (extension) =>
72
+ extension.url ===
73
+ 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-calculatedExpression' &&
74
+ extension.valueExpression?.language === 'text/fhirpath'
75
+ );
76
+ }
77
+
78
+ /**
79
+ * Get answerExpression.valueExpression if its present in item
80
+ *
81
+ * @author Sean Fong
82
+ */
83
+ export function getAnswerExpression(qItem: QuestionnaireItem): Expression | null {
84
+ const itemControl = qItem.extension?.find(
85
+ (extension: Extension) =>
86
+ extension.url ===
87
+ 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-answerExpression' &&
88
+ extension.valueExpression?.language === 'text/fhirpath'
89
+ );
90
+ if (itemControl) {
91
+ if (itemControl.valueExpression) {
92
+ return itemControl.valueExpression;
93
+ }
94
+ }
95
+ return null;
96
+ }
@@ -316,7 +316,7 @@ export interface initialFormFromResponseParams {
316
316
  questionnaireResponse: QuestionnaireResponse;
317
317
  enableWhenItems: EnableWhenItems;
318
318
  enableWhenExpressions: EnableWhenExpressions;
319
- calculatedExpressions: Record<string, CalculatedExpression>;
319
+ calculatedExpressions: Record<string, CalculatedExpression[]>;
320
320
  variablesFhirPath: Record<string, Expression[]>;
321
321
  tabs: Tabs;
322
322
  fhirPathContext: Record<string, any>;
@@ -326,7 +326,7 @@ export function initialiseFormFromResponse(params: initialFormFromResponseParams
326
326
  initialEnableWhenItems: EnableWhenItems;
327
327
  initialEnableWhenLinkedQuestions: Record<string, string[]>;
328
328
  initialEnableWhenExpressions: EnableWhenExpressions;
329
- initialCalculatedExpressions: Record<string, CalculatedExpression>;
329
+ initialCalculatedExpressions: Record<string, CalculatedExpression[]>;
330
330
  firstVisibleTab: number;
331
331
  updatedFhirPathContext: Record<string, any>;
332
332
  } {
@@ -15,7 +15,7 @@
15
15
  * limitations under the License.
16
16
  */
17
17
 
18
- import type { Coding, Expression, Extension, QuestionnaireItem } from 'fhir/r4';
18
+ import type { Coding, Extension, QuestionnaireItem } from 'fhir/r4';
19
19
  import type { RegexValidation } from '../interfaces/regex.interface';
20
20
  import { structuredDataCapture } from 'fhir-sdc-helpers';
21
21
 
@@ -101,26 +101,6 @@ export function hasHiddenExtension(qItem: QuestionnaireItem): boolean {
101
101
  return false;
102
102
  }
103
103
 
104
- /**
105
- * Check if an answerExpression extension is present
106
- *
107
- * @author Sean Fong
108
- */
109
- export function getAnswerExpression(qItem: QuestionnaireItem): Expression | null {
110
- const itemControl = qItem.extension?.find(
111
- (extension: Extension) =>
112
- extension.url ===
113
- 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-answerExpression' &&
114
- extension.valueExpression?.language === 'text/fhirpath'
115
- );
116
- if (itemControl) {
117
- if (itemControl.valueExpression) {
118
- return itemControl.valueExpression;
119
- }
120
- }
121
- return null;
122
- }
123
-
124
104
  /**
125
105
  * Check if the extension has url for items that use open label
126
106
  *
@@ -17,7 +17,6 @@
17
17
 
18
18
  import type {
19
19
  Expression,
20
- Extension,
21
20
  Questionnaire,
22
21
  QuestionnaireItem,
23
22
  QuestionnaireItemEnableWhen
@@ -35,7 +34,6 @@ import type {
35
34
  } from '../../interfaces';
36
35
  import type { AnswerExpression } from '../../interfaces/answerExpression.interface';
37
36
  import type { ValueSetPromise } from '../../interfaces/valueSet.interface';
38
- import { getAnswerExpression } from '../itemControl';
39
37
  import { getTerminologyServerUrl, getValueSetPromise } from '../valueSet';
40
38
  import type { Variables } from '../../interfaces/variables.interface';
41
39
  import { getFhirPathVariables, getXFhirQueryVariables } from './extractVariables';
@@ -44,12 +42,17 @@ import { checkItemIsEnabledRepeat } from '../enableWhen';
44
42
  import cloneDeep from 'lodash.clonedeep';
45
43
  import { emptyResponse } from '../emptyResource';
46
44
  import { evaluateEnableWhenRepeatExpressionInstance } from '../enableWhenExpression';
45
+ import {
46
+ getAnswerExpression,
47
+ getCalculatedExpressions,
48
+ getEnableWhenExpression
49
+ } from '../getExpressionsFromItem';
47
50
 
48
51
  interface ReturnParamsRecursive {
49
52
  variables: Variables;
50
53
  enableWhenItems: EnableWhenItems;
51
54
  enableWhenExpressions: EnableWhenExpressions;
52
- calculatedExpressions: Record<string, CalculatedExpression>;
55
+ calculatedExpressions: Record<string, CalculatedExpression[]>;
53
56
  answerExpressions: Record<string, AnswerExpression>;
54
57
  valueSetPromises: Record<string, ValueSetPromise>;
55
58
  }
@@ -65,7 +68,7 @@ export function extractOtherExtensions(
65
68
  singleExpressions: {},
66
69
  repeatExpressions: {}
67
70
  };
68
- const calculatedExpressions: Record<string, CalculatedExpression> = {};
71
+ const calculatedExpressions: Record<string, CalculatedExpression[]> = {};
69
72
  const answerExpressions: Record<string, AnswerExpression> = {};
70
73
 
71
74
  if (!questionnaire.item || questionnaire.item.length === 0) {
@@ -114,7 +117,7 @@ interface extractExtensionsFromItemRecursiveParams {
114
117
  variables: Variables;
115
118
  enableWhenItems: EnableWhenItems;
116
119
  enableWhenExpressions: EnableWhenExpressions;
117
- calculatedExpressions: Record<string, CalculatedExpression>;
120
+ calculatedExpressions: Record<string, CalculatedExpression[]>;
118
121
  answerExpressions: Record<string, AnswerExpression>;
119
122
  valueSetPromises: Record<string, ValueSetPromise>;
120
123
  defaultTerminologyServerUrl: string;
@@ -184,13 +187,13 @@ function extractExtensionsFromItemRecursive(
184
187
  }
185
188
  }
186
189
 
187
- const calculatedExpression = getCalculatedExpression(item);
188
- if (calculatedExpression) {
189
- calculatedExpressions[item.linkId] = {
190
- expression: `${calculatedExpression.expression}`
191
- };
190
+ // Get calculatedExpressions
191
+ const calculatedExpressionsOfItem = getCalculatedExpressions(item);
192
+ if (calculatedExpressionsOfItem.length > 0) {
193
+ calculatedExpressions[item.linkId] = calculatedExpressionsOfItem;
192
194
  }
193
195
 
196
+ // Get answerExpressions
194
197
  const answerExpression = getAnswerExpression(item);
195
198
  if (answerExpression) {
196
199
  answerExpressions[item.linkId] = {
@@ -417,43 +420,3 @@ function initialiseEnableWhenExpression(
417
420
  }
418
421
  };
419
422
  }
420
-
421
- /**
422
- * Check if an enableWhenExpression extension is present
423
- *
424
- * @author Sean Fong
425
- */
426
- export function getEnableWhenExpression(qItem: QuestionnaireItem): Expression | null {
427
- const itemControl = qItem.extension?.find(
428
- (extension: Extension) =>
429
- extension.url ===
430
- 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-enableWhenExpression' &&
431
- extension.valueExpression?.language === 'text/fhirpath'
432
- );
433
- if (itemControl) {
434
- if (itemControl.valueExpression) {
435
- return itemControl.valueExpression;
436
- }
437
- }
438
- return null;
439
- }
440
-
441
- /**
442
- * Check if an calculatedExpression extension is present
443
- *
444
- * @author Sean Fong
445
- */
446
- export function getCalculatedExpression(qItem: QuestionnaireItem): Expression | null {
447
- const itemControl = qItem.extension?.find(
448
- (extension: Extension) =>
449
- extension.url ===
450
- 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-calculatedExpression' &&
451
- extension.valueExpression?.language === 'text/fhirpath'
452
- );
453
- if (itemControl) {
454
- if (itemControl.valueExpression) {
455
- return itemControl.valueExpression;
456
- }
457
- }
458
- return null;
459
- }
@@ -184,6 +184,10 @@ function answerIsEmpty(
184
184
  return true;
185
185
  }
186
186
 
187
+ if (qrItem.answer.length === 0) {
188
+ return true;
189
+ }
190
+
187
191
  if (qrItem.answer[0]?.valueString === '') {
188
192
  return true;
189
193
  }