@aehrc/smart-forms-renderer 0.25.2 → 0.26.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/lib/components/FormComponents/BooleanItem/BooleanItem.js +6 -1
  2. package/lib/components/FormComponents/BooleanItem/BooleanItem.js.map +1 -1
  3. package/lib/components/FormComponents/ChoiceItems/ChoiceRadioAnswerOptionItem.js +5 -2
  4. package/lib/components/FormComponents/ChoiceItems/ChoiceRadioAnswerOptionItem.js.map +1 -1
  5. package/lib/components/FormComponents/ChoiceItems/ChoiceRadioAnswerValueSetItem.js +5 -2
  6. package/lib/components/FormComponents/ChoiceItems/ChoiceRadioAnswerValueSetItem.js.map +1 -1
  7. package/lib/components/FormComponents/ChoiceItems/ChoiceSelectAnswerOptionItem.js +5 -2
  8. package/lib/components/FormComponents/ChoiceItems/ChoiceSelectAnswerOptionItem.js.map +1 -1
  9. package/lib/components/FormComponents/ChoiceItems/ChoiceSelectAnswerValueSetItem.js +5 -2
  10. package/lib/components/FormComponents/ChoiceItems/ChoiceSelectAnswerValueSetItem.js.map +1 -1
  11. package/lib/components/FormComponents/DecimalItem/DecimalItem.js +9 -3
  12. package/lib/components/FormComponents/DecimalItem/DecimalItem.js.map +1 -1
  13. package/lib/components/FormComponents/IntegerItem/IntegerItem.js +7 -3
  14. package/lib/components/FormComponents/IntegerItem/IntegerItem.js.map +1 -1
  15. package/lib/components/FormComponents/StringItem/StringItem.js +7 -3
  16. package/lib/components/FormComponents/StringItem/StringItem.js.map +1 -1
  17. package/lib/components/FormComponents/TextItem/TextItem.js +7 -3
  18. package/lib/components/FormComponents/TextItem/TextItem.js.map +1 -1
  19. package/lib/components/Renderer/BaseRenderer.js +3 -3
  20. package/lib/components/Renderer/BaseRenderer.js.map +1 -1
  21. package/lib/hooks/useBooleanCalculatedExpression.d.ts +5 -4
  22. package/lib/hooks/useBooleanCalculatedExpression.js +12 -7
  23. package/lib/hooks/useBooleanCalculatedExpression.js.map +1 -1
  24. package/lib/hooks/useCodingCalculatedExpression.d.ts +4 -3
  25. package/lib/hooks/useCodingCalculatedExpression.js +14 -7
  26. package/lib/hooks/useCodingCalculatedExpression.js.map +1 -1
  27. package/lib/hooks/useDecimalCalculatedExpression.d.ts +3 -3
  28. package/lib/hooks/useDecimalCalculatedExpression.js +11 -7
  29. package/lib/hooks/useDecimalCalculatedExpression.js.map +1 -1
  30. package/lib/hooks/useDisplayCalculatedExpression.js +8 -1
  31. package/lib/hooks/useDisplayCalculatedExpression.js.map +1 -1
  32. package/lib/hooks/useIntegerCalculatedExpression.d.ts +3 -3
  33. package/lib/hooks/useIntegerCalculatedExpression.js +11 -7
  34. package/lib/hooks/useIntegerCalculatedExpression.js.map +1 -1
  35. package/lib/hooks/useStringCalculatedExpression.d.ts +3 -3
  36. package/lib/hooks/useStringCalculatedExpression.js +12 -7
  37. package/lib/hooks/useStringCalculatedExpression.js.map +1 -1
  38. package/lib/interfaces/calculatedExpression.interface.d.ts +1 -1
  39. package/lib/stores/questionnaireResponseStore.d.ts +3 -1
  40. package/lib/stores/questionnaireResponseStore.js +9 -1
  41. package/lib/stores/questionnaireResponseStore.js.map +1 -1
  42. package/lib/stores/questionnaireStore.d.ts +6 -2
  43. package/lib/stores/questionnaireStore.js +13 -2
  44. package/lib/stores/questionnaireStore.js.map +1 -1
  45. package/lib/utils/calculatedExpression.d.ts +2 -1
  46. package/lib/utils/calculatedExpression.js +14 -8
  47. package/lib/utils/calculatedExpression.js.map +1 -1
  48. package/lib/utils/enableWhenExpression.d.ts +3 -2
  49. package/lib/utils/enableWhenExpression.js +15 -5
  50. package/lib/utils/enableWhenExpression.js.map +1 -1
  51. package/lib/utils/fhirpath.d.ts +5 -4
  52. package/lib/utils/fhirpath.js +45 -33
  53. package/lib/utils/fhirpath.js.map +1 -1
  54. package/lib/utils/index.d.ts +1 -0
  55. package/lib/utils/index.js +1 -0
  56. package/lib/utils/index.js.map +1 -1
  57. package/lib/utils/initialise.js +5 -2
  58. package/lib/utils/initialise.js.map +1 -1
  59. package/lib/utils/parseInputs.js +1 -1
  60. package/lib/utils/parseInputs.js.map +1 -1
  61. package/package.json +2 -1
  62. package/src/components/FormComponents/BooleanItem/BooleanItem.tsx +9 -1
  63. package/src/components/FormComponents/ChoiceItems/ChoiceRadioAnswerOptionItem.tsx +5 -2
  64. package/src/components/FormComponents/ChoiceItems/ChoiceRadioAnswerValueSetItem.tsx +5 -2
  65. package/src/components/FormComponents/ChoiceItems/ChoiceSelectAnswerOptionItem.tsx +5 -2
  66. package/src/components/FormComponents/ChoiceItems/ChoiceSelectAnswerValueSetItem.tsx +5 -2
  67. package/src/components/FormComponents/DecimalItem/DecimalItem.tsx +14 -3
  68. package/src/components/FormComponents/IntegerItem/IntegerItem.tsx +10 -3
  69. package/src/components/FormComponents/StringItem/StringItem.tsx +10 -3
  70. package/src/components/FormComponents/TextItem/TextItem.tsx +10 -3
  71. package/src/components/Renderer/BaseRenderer.tsx +3 -3
  72. package/src/hooks/useBooleanCalculatedExpression.ts +19 -12
  73. package/src/hooks/useCodingCalculatedExpression.ts +18 -8
  74. package/src/hooks/useDecimalCalculatedExpression.ts +26 -17
  75. package/src/hooks/useDisplayCalculatedExpression.ts +12 -1
  76. package/src/hooks/useIntegerCalculatedExpression.ts +18 -13
  77. package/src/hooks/useStringCalculatedExpression.ts +16 -13
  78. package/src/interfaces/calculatedExpression.interface.ts +1 -1
  79. package/src/stores/questionnaireResponseStore.ts +16 -2
  80. package/src/stores/questionnaireStore.ts +22 -2
  81. package/src/utils/calculatedExpression.ts +24 -10
  82. package/src/utils/enableWhenExpression.ts +22 -9
  83. package/src/utils/fhirpath.ts +62 -41
  84. package/src/utils/index.ts +1 -0
  85. package/src/utils/initialise.ts +5 -2
  86. package/src/utils/parseInputs.ts +1 -1
  87. package/src/utils/questionnaireResponseStoreUtils/updatableResponseItems.ts +62 -0
@@ -25,6 +25,7 @@ import { evaluateCalculatedExpressions } from './calculatedExpression';
25
25
 
26
26
  interface EvaluateUpdatedExpressionsParams {
27
27
  updatedResponse: QuestionnaireResponse;
28
+ updatedResponseItemMap: Record<string, QuestionnaireResponseItem[]>;
28
29
  calculatedExpressions: Record<string, CalculatedExpression[]>;
29
30
  enableWhenExpressions: EnableWhenExpressions;
30
31
  variablesFhirPath: Record<string, Expression[]>;
@@ -39,6 +40,7 @@ export function evaluateUpdatedExpressions(params: EvaluateUpdatedExpressionsPar
39
40
  } {
40
41
  const {
41
42
  updatedResponse,
43
+ updatedResponseItemMap,
42
44
  enableWhenExpressions,
43
45
  calculatedExpressions,
44
46
  variablesFhirPath,
@@ -59,8 +61,8 @@ export function evaluateUpdatedExpressions(params: EvaluateUpdatedExpressionsPar
59
61
 
60
62
  const updatedFhirPathContext = createFhirPathContext(
61
63
  updatedResponse,
62
- variablesFhirPath,
63
- existingFhirPathContext
64
+ updatedResponseItemMap,
65
+ variablesFhirPath
64
66
  );
65
67
 
66
68
  // Update enableWhenExpressions
@@ -87,58 +89,52 @@ export function evaluateUpdatedExpressions(params: EvaluateUpdatedExpressionsPar
87
89
 
88
90
  export function createFhirPathContext(
89
91
  questionnaireResponse: QuestionnaireResponse,
90
- variablesFhirPath: Record<string, Expression[]>,
91
- existingFhirPathContext: Record<string, any>
92
+ questionnaireResponseItemMap: Record<string, QuestionnaireResponseItem[]>,
93
+ variablesFhirPath: Record<string, Expression[]>
92
94
  ): Record<string, any> {
93
- let fhirPathContext = {};
94
- if (Object.keys(existingFhirPathContext).length > 0) {
95
- fhirPathContext = { ...fhirPathContext, ...existingFhirPathContext };
95
+ if (!questionnaireResponse.item) {
96
+ return {};
96
97
  }
97
98
 
98
99
  // Add latest resource to fhirPathContext
99
- fhirPathContext = { ...fhirPathContext, resource: questionnaireResponse };
100
-
101
- if (!questionnaireResponse.item || questionnaireResponse.item.length === 0) {
102
- return fhirPathContext;
103
- }
100
+ let fhirPathContext: Record<string, any> = { resource: questionnaireResponse };
104
101
 
105
- evaluateResourceLevelFhirPath(questionnaireResponse, variablesFhirPath, fhirPathContext);
102
+ // Evaluate resource-level variables
103
+ fhirPathContext = evaluateQuestionnaireLevelVariables(
104
+ questionnaireResponse,
105
+ variablesFhirPath,
106
+ fhirPathContext
107
+ );
106
108
 
107
- for (const topLevelItem of questionnaireResponse.item) {
108
- evaluateFhirPathRecursive(topLevelItem, variablesFhirPath, fhirPathContext);
109
+ // Add variables of items that exist in questionnaireResponseItemMap into fhirPathContext
110
+ for (const linkId in questionnaireResponseItemMap) {
111
+ // For non-repeat groups, the same linkId will have only one item
112
+ // For repeat groups, the same linkId will have multiple items
113
+ for (const qrItem of questionnaireResponseItemMap[linkId]) {
114
+ fhirPathContext = evaluateLinkIdVariables(qrItem, variablesFhirPath, fhirPathContext);
115
+ }
109
116
  }
110
117
 
111
- return fhirPathContext;
112
- }
113
-
114
- export function evaluateFhirPathRecursive(
115
- item: QuestionnaireResponseItem,
116
- variablesFhirPath: Record<string, Expression[]>,
117
- fhirPathContext: Record<string, any>
118
- ) {
119
- const items = item.item;
120
- if (items && items.length > 0) {
121
- // iterate through items of item recursively
122
- for (const childItem of items) {
123
- evaluateFhirPathRecursive(childItem, variablesFhirPath, fhirPathContext);
124
- }
118
+ // Items don't exist in questionnaireResponseItemMap, but we still have to add them into the fhirPathContext as empty arrays
119
+ for (const linkId in variablesFhirPath) {
120
+ fhirPathContext = addEmptyLinkIdVariables(linkId, variablesFhirPath, fhirPathContext);
125
121
  }
126
122
 
127
- evaluateItemFhirPath(item, variablesFhirPath, fhirPathContext);
123
+ return fhirPathContext;
128
124
  }
129
125
 
130
- export function evaluateItemFhirPath(
126
+ export function evaluateLinkIdVariables(
131
127
  item: QuestionnaireResponseItem,
132
128
  variablesFhirPath: Record<string, Expression[]>,
133
129
  fhirPathContext: Record<string, any>
134
130
  ) {
135
- const variablesOfItem = variablesFhirPath[item.linkId];
136
- if (!variablesOfItem || variablesOfItem.length === 0) {
137
- return;
131
+ const linkIdVariables = variablesFhirPath[item.linkId];
132
+ if (!linkIdVariables || linkIdVariables.length === 0) {
133
+ return fhirPathContext;
138
134
  }
139
135
 
140
- variablesOfItem.forEach((variable) => {
141
- if (variable.expression) {
136
+ for (const variable of linkIdVariables) {
137
+ if (variable.expression && variable.name) {
142
138
  try {
143
139
  fhirPathContext[`${variable.name}`] = fhirpath.evaluate(
144
140
  item,
@@ -153,20 +149,43 @@ export function evaluateItemFhirPath(
153
149
  console.warn(e.message, `LinkId: ${item.linkId}\nExpression: ${variable.expression}`);
154
150
  }
155
151
  }
156
- });
152
+ }
153
+
154
+ return fhirPathContext;
155
+ }
156
+
157
+ export function addEmptyLinkIdVariables(
158
+ linkId: string,
159
+ variablesFhirPath: Record<string, Expression[]>,
160
+ fhirPathContext: Record<string, any>
161
+ ) {
162
+ const linkIdVariables = variablesFhirPath[linkId];
163
+ if (!linkIdVariables || linkIdVariables.length === 0) {
164
+ return fhirPathContext;
165
+ }
166
+
167
+ for (const variable of linkIdVariables) {
168
+ if (variable.expression && variable.name) {
169
+ if (fhirPathContext[`${variable.name}`] === undefined) {
170
+ fhirPathContext[`${variable.name}`] = [];
171
+ }
172
+ }
173
+ }
174
+
175
+ return fhirPathContext;
157
176
  }
158
177
 
159
- export function evaluateResourceLevelFhirPath(
178
+ export function evaluateQuestionnaireLevelVariables(
160
179
  resource: QuestionnaireResponse,
161
180
  variablesFhirPath: Record<string, Expression[]>,
162
181
  fhirPathContext: Record<string, any>
163
182
  ) {
164
183
  const questionnaireLevelVariables = variablesFhirPath['QuestionnaireLevel'];
165
184
  if (!questionnaireLevelVariables || questionnaireLevelVariables.length === 0) {
166
- return;
185
+ return fhirPathContext;
167
186
  }
168
187
 
169
- questionnaireLevelVariables.forEach((variable) => {
188
+ for (const variable of questionnaireLevelVariables) {
170
189
  if (variable.expression) {
171
190
  try {
172
191
  fhirPathContext[`${variable.name}`] = fhirpath.evaluate(
@@ -182,5 +201,7 @@ export function evaluateResourceLevelFhirPath(
182
201
  console.warn(e.message, `Questionnaire-level\nExpression: ${variable.expression}`);
183
202
  }
184
203
  }
185
- });
204
+ }
205
+
206
+ return fhirPathContext;
186
207
  }
@@ -22,3 +22,4 @@ export {
22
22
  readInitialAnswers,
23
23
  setInitialAnswers
24
24
  } from './enableWhen';
25
+ export { initialiseQuestionnaireResponse } from './initialise';
@@ -31,6 +31,7 @@ import type { Tabs } from '../interfaces/tab.interface';
31
31
  import { assignPopulatedAnswersToEnableWhen } from './enableWhen';
32
32
  import type { CalculatedExpression } from '../interfaces/calculatedExpression.interface';
33
33
  import { evaluateInitialCalculatedExpressions } from './calculatedExpression';
34
+ import { createQuestionnaireResponseItemMap } from './questionnaireResponseStoreUtils/updatableResponseItems';
34
35
 
35
36
  /**
36
37
  * Initialise a conformant questionnaireResponse from a given questionnaire
@@ -345,6 +346,7 @@ export function initialiseFormFromResponse(params: initialFormFromResponseParams
345
346
  variablesFhirPath,
346
347
  tabs
347
348
  } = params;
349
+ const initialResponseItemMap = createQuestionnaireResponseItemMap(questionnaireResponse);
348
350
  let updatedFhirPathContext = params.fhirPathContext;
349
351
 
350
352
  const { initialisedItems, linkedQuestions } = assignPopulatedAnswersToEnableWhen(
@@ -354,15 +356,16 @@ export function initialiseFormFromResponse(params: initialFormFromResponseParams
354
356
 
355
357
  const evaluateInitialEnableWhenExpressionsResult = evaluateInitialEnableWhenExpressions({
356
358
  initialResponse: questionnaireResponse,
359
+ initialResponseItemMap: initialResponseItemMap,
357
360
  enableWhenExpressions: enableWhenExpressions,
358
- variablesFhirPath: variablesFhirPath,
359
- existingFhirPathContext: updatedFhirPathContext
361
+ variablesFhirPath: variablesFhirPath
360
362
  });
361
363
  const { initialEnableWhenExpressions } = evaluateInitialEnableWhenExpressionsResult;
362
364
  updatedFhirPathContext = evaluateInitialEnableWhenExpressionsResult.updatedFhirPathContext;
363
365
 
364
366
  const evaluateInitialCalculatedExpressionsResult = evaluateInitialCalculatedExpressions({
365
367
  initialResponse: questionnaireResponse,
368
+ initialResponseItemMap: initialResponseItemMap,
366
369
  calculatedExpressions: calculatedExpressions,
367
370
  variablesFhirPath: variablesFhirPath,
368
371
  existingFhirPathContext: updatedFhirPathContext
@@ -59,7 +59,7 @@ export function parseDecimalStringWithPrecision(input: string, precision: number
59
59
 
60
60
  // truncate decimal digits based on precision
61
61
  const parsedDecimalPoint = input.indexOf('.');
62
- if (precision && parsedDecimalPoint !== -1) {
62
+ if (typeof precision === 'number' && parsedDecimalPoint !== -1) {
63
63
  parsedInput = parsedInput.substring(0, parsedDecimalPoint + precision + 1);
64
64
  }
65
65
 
@@ -0,0 +1,62 @@
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 { QuestionnaireResponse, QuestionnaireResponseItem } from 'fhir/r4';
19
+
20
+ export function createQuestionnaireResponseItemMap(
21
+ questionnaireResponse: QuestionnaireResponse
22
+ ): Record<string, QuestionnaireResponseItem[]> {
23
+ if (!questionnaireResponse.item) {
24
+ return {};
25
+ }
26
+
27
+ const questionnaireResponseItemMap: Record<string, QuestionnaireResponseItem[]> = {};
28
+ for (const topLevelQRItem of questionnaireResponse.item) {
29
+ fillQuestionnaireResponseItemMapRecursive(topLevelQRItem, questionnaireResponseItemMap);
30
+ }
31
+
32
+ return questionnaireResponseItemMap;
33
+ }
34
+
35
+ function fillQuestionnaireResponseItemMapRecursive(
36
+ qrItem: QuestionnaireResponseItem,
37
+ questionnaireResponseItemMap: Record<string, QuestionnaireResponseItem[]>
38
+ ) {
39
+ const qrItems = qrItem.item;
40
+ if (qrItems && qrItems.length > 0) {
41
+ // iterate through items of item recursively
42
+ for (const childQRItem of qrItems) {
43
+ fillQuestionnaireResponseItemMapRecursive(childQRItem, questionnaireResponseItemMap);
44
+ }
45
+ }
46
+
47
+ fillQuestionnaireResponseItemMap(qrItem, questionnaireResponseItemMap);
48
+ }
49
+
50
+ function fillQuestionnaireResponseItemMap(
51
+ qrItem: QuestionnaireResponseItem,
52
+ questionnaireResponseItemMap: Record<string, QuestionnaireResponseItem[]>
53
+ ) {
54
+ // linkId already exists in questionnaireResponseItemMap, it would be a repeat group
55
+ if (qrItem.linkId in questionnaireResponseItemMap) {
56
+ questionnaireResponseItemMap[qrItem.linkId].push(qrItem);
57
+ }
58
+ // Add item to questionnaireResponseItemMap
59
+ else {
60
+ questionnaireResponseItemMap[qrItem.linkId] = [qrItem];
61
+ }
62
+ }