@aehrc/smart-forms-renderer 0.37.2 → 0.38.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.
- package/lib/components/FormComponents/QuantityItem/QuantityComparatorField.d.ts +12 -0
- package/lib/components/FormComponents/QuantityItem/QuantityComparatorField.js +13 -0
- package/lib/components/FormComponents/QuantityItem/QuantityComparatorField.js.map +1 -0
- package/lib/components/FormComponents/QuantityItem/QuantityField.d.ts +15 -0
- package/lib/components/FormComponents/QuantityItem/QuantityField.js +14 -0
- package/lib/components/FormComponents/QuantityItem/QuantityField.js.map +1 -0
- package/lib/components/FormComponents/QuantityItem/QuantityItem.d.ts +9 -0
- package/lib/components/FormComponents/QuantityItem/QuantityItem.js +144 -0
- package/lib/components/FormComponents/QuantityItem/QuantityItem.js.map +1 -0
- package/lib/components/FormComponents/QuantityItem/QuantityUnitField.d.ts +12 -0
- package/lib/components/FormComponents/QuantityItem/QuantityUnitField.js +10 -0
- package/lib/components/FormComponents/QuantityItem/QuantityUnitField.js.map +1 -0
- package/lib/components/FormComponents/SingleItem/SingleItemSwitcher.js +2 -1
- package/lib/components/FormComponents/SingleItem/SingleItemSwitcher.js.map +1 -1
- package/lib/hooks/useDecimalCalculatedExpression.d.ts +2 -2
- package/lib/hooks/useQuantityCalculatedExpression.d.ts +14 -0
- package/lib/hooks/useQuantityCalculatedExpression.js +105 -0
- package/lib/hooks/useQuantityCalculatedExpression.js.map +1 -0
- package/lib/hooks/useRenderingExtensions.d.ts +2 -1
- package/lib/hooks/useRenderingExtensions.js +3 -2
- package/lib/hooks/useRenderingExtensions.js.map +1 -1
- package/lib/hooks/useStringInput.js +1 -0
- package/lib/hooks/useStringInput.js.map +1 -1
- package/lib/interfaces/valueSet.interface.d.ts +15 -0
- package/lib/utils/calculatedExpression.js +4 -1
- package/lib/utils/calculatedExpression.js.map +1 -1
- package/lib/utils/itemControl.d.ts +7 -1
- package/lib/utils/itemControl.js +14 -0
- package/lib/utils/itemControl.js.map +1 -1
- package/lib/utils/quantity.d.ts +4 -0
- package/lib/utils/quantity.js +49 -0
- package/lib/utils/quantity.js.map +1 -0
- package/lib/utils/valueSet.d.ts +2 -1
- package/lib/utils/valueSet.js +22 -0
- package/lib/utils/valueSet.js.map +1 -1
- package/package.json +1 -1
- package/src/components/FormComponents/QuantityItem/QuantityComparatorField.tsx +40 -0
- package/src/components/FormComponents/QuantityItem/QuantityField.tsx +60 -0
- package/src/components/FormComponents/QuantityItem/QuantityItem.tsx +286 -0
- package/src/components/FormComponents/QuantityItem/QuantityUnitField.tsx +38 -0
- package/src/components/FormComponents/SingleItem/SingleItemSwitcher.tsx +2 -1
- package/src/hooks/useDecimalCalculatedExpression.ts +2 -2
- package/src/hooks/useQuantityCalculatedExpression.ts +177 -0
- package/src/hooks/useRenderingExtensions.ts +5 -2
- package/src/hooks/useStringInput.ts +1 -0
- package/src/interfaces/valueSet.interface.ts +19 -0
- package/src/stories/assets/questionnaires/QPrePopTester.ts +30 -0
- package/src/stories/assets/questionnaires/QQuantity.ts +283 -1
- package/src/stories/itemTypes/Quantity.stories.tsx +33 -1
- package/src/utils/calculatedExpression.ts +5 -1
- package/src/utils/itemControl.ts +19 -1
- package/src/utils/quantity.ts +62 -0
- package/src/utils/valueSet.ts +32 -1
|
@@ -0,0 +1,177 @@
|
|
|
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 { useEffect, useState } from 'react';
|
|
19
|
+
import type { QuestionnaireItem } from 'fhir/r4';
|
|
20
|
+
import { useQuestionnaireStore } from '../stores';
|
|
21
|
+
import { validateCodePromise } from '../utils/valueSet';
|
|
22
|
+
import { TERMINOLOGY_SERVER_URL } from '../globals';
|
|
23
|
+
import type {
|
|
24
|
+
CodeParameter,
|
|
25
|
+
DisplayParameter,
|
|
26
|
+
SystemParameter
|
|
27
|
+
} from '../interfaces/valueSet.interface';
|
|
28
|
+
|
|
29
|
+
interface UseQuantityCalculatedExpression {
|
|
30
|
+
calcExpUpdated: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface UseQuantityCalculatedExpressionProps {
|
|
34
|
+
qItem: QuestionnaireItem;
|
|
35
|
+
inputValue: string;
|
|
36
|
+
precision: number | null;
|
|
37
|
+
onChangeByCalcExpressionDecimal: (newValue: number) => void;
|
|
38
|
+
onChangeByCalcExpressionQuantity: (
|
|
39
|
+
newValue: number,
|
|
40
|
+
newUnitSystem: string,
|
|
41
|
+
newUnitCode: string,
|
|
42
|
+
newUnitDisplay: string
|
|
43
|
+
) => void;
|
|
44
|
+
onChangeByCalcExpressionNull: () => void;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function useQuantityCalculatedExpression(
|
|
48
|
+
props: UseQuantityCalculatedExpressionProps
|
|
49
|
+
): UseQuantityCalculatedExpression {
|
|
50
|
+
const {
|
|
51
|
+
qItem,
|
|
52
|
+
inputValue,
|
|
53
|
+
precision,
|
|
54
|
+
onChangeByCalcExpressionDecimal,
|
|
55
|
+
onChangeByCalcExpressionQuantity,
|
|
56
|
+
onChangeByCalcExpressionNull
|
|
57
|
+
} = props;
|
|
58
|
+
|
|
59
|
+
const calculatedExpressions = useQuestionnaireStore.use.calculatedExpressions();
|
|
60
|
+
|
|
61
|
+
const [calcExpUpdated, setCalcExpUpdated] = useState(false);
|
|
62
|
+
|
|
63
|
+
useEffect(
|
|
64
|
+
() => {
|
|
65
|
+
const calcExpression = calculatedExpressions[qItem.linkId]?.find(
|
|
66
|
+
(exp) => exp.from === 'item'
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
if (!calcExpression) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// only update if calculated value is different from current value
|
|
74
|
+
if (
|
|
75
|
+
calcExpression.value !== inputValue &&
|
|
76
|
+
(typeof calcExpression.value === 'number' ||
|
|
77
|
+
typeof calcExpression.value === 'string' ||
|
|
78
|
+
calcExpression.value === null)
|
|
79
|
+
) {
|
|
80
|
+
// Null path
|
|
81
|
+
if (calcExpression.value === null) {
|
|
82
|
+
onChangeByCalcExpressionNull();
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Number path
|
|
87
|
+
if (typeof calcExpression.value === 'number') {
|
|
88
|
+
const calcExpressionValue =
|
|
89
|
+
typeof precision === 'number'
|
|
90
|
+
? parseFloat(calcExpression.value.toFixed(precision))
|
|
91
|
+
: calcExpression.value;
|
|
92
|
+
|
|
93
|
+
// only update if calculated value is different from current value
|
|
94
|
+
if (calcExpressionValue !== parseFloat(inputValue)) {
|
|
95
|
+
// update ui to show calculated value changes
|
|
96
|
+
setCalcExpUpdated(true);
|
|
97
|
+
setTimeout(() => {
|
|
98
|
+
setCalcExpUpdated(false);
|
|
99
|
+
}, 500);
|
|
100
|
+
|
|
101
|
+
// calculatedExpression value is null
|
|
102
|
+
if (calcExpressionValue === null) {
|
|
103
|
+
onChangeByCalcExpressionNull();
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// calculatedExpression value is a number
|
|
108
|
+
onChangeByCalcExpressionDecimal(calcExpressionValue);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// String path (quantity)
|
|
113
|
+
if (typeof calcExpression.value === 'string') {
|
|
114
|
+
try {
|
|
115
|
+
const [value, unitCode] = calcExpression.value.split(' ');
|
|
116
|
+
const unitCodeFormatted = unitCode.replace(/'/g, '');
|
|
117
|
+
|
|
118
|
+
const ucumValueSet = 'http://hl7.org/fhir/ValueSet/ucum-units';
|
|
119
|
+
const ucumSystem = 'http://unitsofmeasure.org';
|
|
120
|
+
|
|
121
|
+
validateCodePromise(
|
|
122
|
+
ucumValueSet,
|
|
123
|
+
ucumSystem,
|
|
124
|
+
unitCodeFormatted,
|
|
125
|
+
TERMINOLOGY_SERVER_URL
|
|
126
|
+
).then((validateCodeResponse) => {
|
|
127
|
+
// Return early if validate-code request fails
|
|
128
|
+
if (!validateCodeResponse) {
|
|
129
|
+
onChangeByCalcExpressionNull();
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (validateCodeResponse.parameter) {
|
|
134
|
+
const systemParameter = validateCodeResponse.parameter.find(
|
|
135
|
+
(p) => p.name === 'system'
|
|
136
|
+
) as SystemParameter;
|
|
137
|
+
const codeParameter = validateCodeResponse.parameter.find(
|
|
138
|
+
(p) => p.name === 'code'
|
|
139
|
+
) as CodeParameter;
|
|
140
|
+
const displayParameter = validateCodeResponse.parameter.find(
|
|
141
|
+
(p) => p.name === 'display'
|
|
142
|
+
) as DisplayParameter;
|
|
143
|
+
if (
|
|
144
|
+
systemParameter.valueUri &&
|
|
145
|
+
codeParameter.valueCode &&
|
|
146
|
+
displayParameter.valueString
|
|
147
|
+
) {
|
|
148
|
+
// update ui to show calculated value changes
|
|
149
|
+
setCalcExpUpdated(true);
|
|
150
|
+
setTimeout(() => {
|
|
151
|
+
setCalcExpUpdated(false);
|
|
152
|
+
}, 500);
|
|
153
|
+
onChangeByCalcExpressionQuantity(
|
|
154
|
+
parseFloat(value),
|
|
155
|
+
systemParameter.valueUri,
|
|
156
|
+
codeParameter.valueCode,
|
|
157
|
+
displayParameter.valueString
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
} catch (e) {
|
|
163
|
+
console.error(e);
|
|
164
|
+
onChangeByCalcExpressionNull();
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
// Only trigger this effect if calculatedExpression of item changes
|
|
170
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
171
|
+
[calculatedExpressions]
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
return { calcExpUpdated: calcExpUpdated };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export default useQuantityCalculatedExpression;
|
|
@@ -16,12 +16,13 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import {
|
|
19
|
+
getQuantityUnit,
|
|
19
20
|
getTextDisplayFlyover,
|
|
20
21
|
getTextDisplayInstructions,
|
|
21
22
|
getTextDisplayPrompt,
|
|
22
23
|
getTextDisplayUnit
|
|
23
24
|
} from '../utils/itemControl';
|
|
24
|
-
import type { QuestionnaireItem } from 'fhir/r4';
|
|
25
|
+
import type { QuestionnaireItem, QuestionnaireItemAnswerOption } from 'fhir/r4';
|
|
25
26
|
import { structuredDataCapture } from 'fhir-sdc-helpers';
|
|
26
27
|
import { useMemo } from 'react';
|
|
27
28
|
|
|
@@ -33,6 +34,7 @@ interface RenderingExtensions {
|
|
|
33
34
|
readOnly: boolean;
|
|
34
35
|
entryFormat: string;
|
|
35
36
|
required: boolean;
|
|
37
|
+
quantityUnit: QuestionnaireItemAnswerOption | null;
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
function useRenderingExtensions(qItem: QuestionnaireItem): RenderingExtensions {
|
|
@@ -44,7 +46,8 @@ function useRenderingExtensions(qItem: QuestionnaireItem): RenderingExtensions {
|
|
|
44
46
|
displayFlyover: getTextDisplayFlyover(qItem),
|
|
45
47
|
readOnly: !!qItem.readOnly,
|
|
46
48
|
entryFormat: structuredDataCapture.getEntryFormat(qItem) ?? '',
|
|
47
|
-
required: qItem.required ?? false
|
|
49
|
+
required: qItem.required ?? false,
|
|
50
|
+
quantityUnit: getQuantityUnit(qItem)
|
|
48
51
|
}),
|
|
49
52
|
[qItem]
|
|
50
53
|
);
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
import type { Dispatch, SetStateAction } from 'react';
|
|
19
19
|
import { useEffect, useState } from 'react';
|
|
20
20
|
|
|
21
|
+
// The purpose of this hook to sync the string state from external changes i.e. re-population changes etc.
|
|
21
22
|
function useStringInput(valueFromProps: string): [string, Dispatch<SetStateAction<string>>] {
|
|
22
23
|
const [input, setInput] = useState(valueFromProps);
|
|
23
24
|
|
|
@@ -21,3 +21,22 @@ export interface ValueSetPromise {
|
|
|
21
21
|
promise: Promise<ValueSet>;
|
|
22
22
|
valueSet?: ValueSet;
|
|
23
23
|
}
|
|
24
|
+
|
|
25
|
+
export interface ValidateCodeResponse extends Parameters<any> {
|
|
26
|
+
parameter: [SystemParameter, CodeParameter, DisplayParameter];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface SystemParameter {
|
|
30
|
+
name: 'system';
|
|
31
|
+
valueUri: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface CodeParameter {
|
|
35
|
+
name: 'code';
|
|
36
|
+
valueCode: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface DisplayParameter {
|
|
40
|
+
name: 'display';
|
|
41
|
+
valueString: string;
|
|
42
|
+
}
|
|
@@ -184,6 +184,14 @@ export const qSelectivePrePopTester: Questionnaire = {
|
|
|
184
184
|
}
|
|
185
185
|
]
|
|
186
186
|
},
|
|
187
|
+
{
|
|
188
|
+
url: 'http://hl7.org/fhir/StructureDefinition/variable',
|
|
189
|
+
valueExpression: {
|
|
190
|
+
name: 'ObsBloodPressure',
|
|
191
|
+
language: 'application/x-fhir-query',
|
|
192
|
+
expression: 'Observation?code=75367002&_count=1&_sort=-date&patient={{%patient.id}}'
|
|
193
|
+
}
|
|
194
|
+
},
|
|
187
195
|
{
|
|
188
196
|
url: 'http://hl7.org/fhir/StructureDefinition/variable',
|
|
189
197
|
valueExpression: {
|
|
@@ -424,6 +432,28 @@ export const qSelectivePrePopTester: Questionnaire = {
|
|
|
424
432
|
}
|
|
425
433
|
]
|
|
426
434
|
},
|
|
435
|
+
{
|
|
436
|
+
linkId: 'blood-pressure-unit-fixed',
|
|
437
|
+
text: 'Blood Pressure',
|
|
438
|
+
type: 'quantity',
|
|
439
|
+
extension: [
|
|
440
|
+
{
|
|
441
|
+
url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression',
|
|
442
|
+
valueExpression: {
|
|
443
|
+
language: 'text/fhirpath',
|
|
444
|
+
expression: '%ObsBloodPressure.entry[0].resource.component[0].value'
|
|
445
|
+
}
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-unit',
|
|
449
|
+
valueCoding: {
|
|
450
|
+
system: 'http://unitsofmeasure.org',
|
|
451
|
+
code: 'mm[Hg]',
|
|
452
|
+
display: 'mmHg'
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
]
|
|
456
|
+
},
|
|
427
457
|
{
|
|
428
458
|
extension: [
|
|
429
459
|
{
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import type { Questionnaire } from 'fhir/r4';
|
|
18
|
+
import type { Questionnaire, QuestionnaireResponse } from 'fhir/r4';
|
|
19
19
|
|
|
20
20
|
export const qQuantityBasic: Questionnaire = {
|
|
21
21
|
resourceType: 'Questionnaire',
|
|
@@ -39,6 +39,288 @@ export const qQuantityBasic: Questionnaire = {
|
|
|
39
39
|
type: 'quantity',
|
|
40
40
|
repeats: false,
|
|
41
41
|
text: 'Body Weight'
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
extension: [
|
|
45
|
+
{
|
|
46
|
+
url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-unit',
|
|
47
|
+
valueCoding: { system: 'http://unitsofmeasure.org', code: 'kg', display: 'kg' }
|
|
48
|
+
}
|
|
49
|
+
],
|
|
50
|
+
linkId: 'body-weight-comparator',
|
|
51
|
+
type: 'quantity',
|
|
52
|
+
repeats: false,
|
|
53
|
+
text: 'Body Weight (with comparator symbol)'
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const qrQuantityBasicResponse: QuestionnaireResponse = {
|
|
59
|
+
resourceType: 'QuestionnaireResponse',
|
|
60
|
+
status: 'in-progress',
|
|
61
|
+
item: [
|
|
62
|
+
{
|
|
63
|
+
linkId: 'body-weight',
|
|
64
|
+
answer: [
|
|
65
|
+
{
|
|
66
|
+
valueQuantity: {
|
|
67
|
+
value: 80,
|
|
68
|
+
unit: 'kg',
|
|
69
|
+
system: 'http://unitsofmeasure.org',
|
|
70
|
+
code: 'kg'
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
],
|
|
74
|
+
text: 'Body Weight'
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
linkId: 'body-weight-comparator',
|
|
78
|
+
answer: [
|
|
79
|
+
{
|
|
80
|
+
valueQuantity: {
|
|
81
|
+
value: 90,
|
|
82
|
+
comparator: '<',
|
|
83
|
+
unit: 'kg',
|
|
84
|
+
system: 'http://unitsofmeasure.org',
|
|
85
|
+
code: 'kg'
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
],
|
|
89
|
+
text: 'Body Weight (with comparator symbol)'
|
|
90
|
+
}
|
|
91
|
+
],
|
|
92
|
+
questionnaire: 'https://smartforms.csiro.au/docs/components/quantity/basic'
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export const qQuantityUnitOption: Questionnaire = {
|
|
96
|
+
resourceType: 'Questionnaire',
|
|
97
|
+
id: 'QuantityUnitOption',
|
|
98
|
+
name: 'QuantityUnitOption',
|
|
99
|
+
title: 'Quantity UnitOption',
|
|
100
|
+
version: '0.1.0',
|
|
101
|
+
status: 'draft',
|
|
102
|
+
publisher: 'AEHRC CSIRO',
|
|
103
|
+
date: '2024-07-27',
|
|
104
|
+
url: 'https://smartforms.csiro.au/docs/components/quantity/unit-option',
|
|
105
|
+
item: [
|
|
106
|
+
{
|
|
107
|
+
linkId: 'duration',
|
|
108
|
+
text: 'Duration',
|
|
109
|
+
type: 'quantity',
|
|
110
|
+
extension: [
|
|
111
|
+
{
|
|
112
|
+
url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-unitOption',
|
|
113
|
+
valueCoding: {
|
|
114
|
+
system: 'http://unitsofmeasure.org',
|
|
115
|
+
code: 'd',
|
|
116
|
+
display: 'Day(s)'
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-unitOption',
|
|
121
|
+
valueCoding: {
|
|
122
|
+
system: 'http://unitsofmeasure.org',
|
|
123
|
+
code: 'wk',
|
|
124
|
+
display: 'Week(s)'
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-unitOption',
|
|
129
|
+
valueCoding: {
|
|
130
|
+
system: 'http://unitsofmeasure.org',
|
|
131
|
+
code: 'mo',
|
|
132
|
+
display: 'Month(s)'
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-unitOption',
|
|
137
|
+
valueCoding: {
|
|
138
|
+
system: 'http://unitsofmeasure.org',
|
|
139
|
+
code: 'a',
|
|
140
|
+
display: 'Year(s)'
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-unitOption',
|
|
145
|
+
valueCoding: {
|
|
146
|
+
system: 'http://unitsofmeasure.org',
|
|
147
|
+
code: 's',
|
|
148
|
+
display: 'Second(s)'
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-unitOption',
|
|
153
|
+
valueCoding: {
|
|
154
|
+
system: 'http://unitsofmeasure.org',
|
|
155
|
+
code: 'min',
|
|
156
|
+
display: 'Minute(s)'
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-unitOption',
|
|
161
|
+
valueCoding: {
|
|
162
|
+
system: 'http://unitsofmeasure.org',
|
|
163
|
+
code: 'hour',
|
|
164
|
+
display: 'Hour(s)'
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
]
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
linkId: 'duration-comparator',
|
|
171
|
+
text: 'Duration (with comparator symbol)',
|
|
172
|
+
type: 'quantity',
|
|
173
|
+
extension: [
|
|
174
|
+
{
|
|
175
|
+
url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-unitOption',
|
|
176
|
+
valueCoding: {
|
|
177
|
+
system: 'http://unitsofmeasure.org',
|
|
178
|
+
code: 'd',
|
|
179
|
+
display: 'Day(s)'
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-unitOption',
|
|
184
|
+
valueCoding: {
|
|
185
|
+
system: 'http://unitsofmeasure.org',
|
|
186
|
+
code: 'wk',
|
|
187
|
+
display: 'Week(s)'
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-unitOption',
|
|
192
|
+
valueCoding: {
|
|
193
|
+
system: 'http://unitsofmeasure.org',
|
|
194
|
+
code: 'mo',
|
|
195
|
+
display: 'Month(s)'
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-unitOption',
|
|
200
|
+
valueCoding: {
|
|
201
|
+
system: 'http://unitsofmeasure.org',
|
|
202
|
+
code: 'a',
|
|
203
|
+
display: 'Year(s)'
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-unitOption',
|
|
208
|
+
valueCoding: {
|
|
209
|
+
system: 'http://unitsofmeasure.org',
|
|
210
|
+
code: 's',
|
|
211
|
+
display: 'Second(s)'
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-unitOption',
|
|
216
|
+
valueCoding: {
|
|
217
|
+
system: 'http://unitsofmeasure.org',
|
|
218
|
+
code: 'min',
|
|
219
|
+
display: 'Minute(s)'
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-unitOption',
|
|
224
|
+
valueCoding: {
|
|
225
|
+
system: 'http://unitsofmeasure.org',
|
|
226
|
+
code: 'hour',
|
|
227
|
+
display: 'Hour(s)'
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
]
|
|
231
|
+
}
|
|
232
|
+
]
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
export const qrQuantityUnitOptionResponse: QuestionnaireResponse = {
|
|
236
|
+
resourceType: 'QuestionnaireResponse',
|
|
237
|
+
status: 'in-progress',
|
|
238
|
+
item: [
|
|
239
|
+
{
|
|
240
|
+
linkId: 'duration',
|
|
241
|
+
answer: [
|
|
242
|
+
{
|
|
243
|
+
valueQuantity: {
|
|
244
|
+
value: 48,
|
|
245
|
+
unit: 'Hour(s)',
|
|
246
|
+
system: 'http://unitsofmeasure.org',
|
|
247
|
+
code: 'hour'
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
],
|
|
251
|
+
text: 'Duration'
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
linkId: 'duration-comparator',
|
|
255
|
+
answer: [
|
|
256
|
+
{
|
|
257
|
+
valueQuantity: {
|
|
258
|
+
value: 48,
|
|
259
|
+
comparator: '>=',
|
|
260
|
+
unit: 'Hour(s)',
|
|
261
|
+
system: 'http://unitsofmeasure.org',
|
|
262
|
+
code: 'hour'
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
],
|
|
266
|
+
text: 'Duration'
|
|
267
|
+
}
|
|
268
|
+
],
|
|
269
|
+
questionnaire: 'https://smartforms.csiro.au/docs/components/quantity/unit-option'
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
export const qQuantityCalculation: Questionnaire = {
|
|
273
|
+
resourceType: 'Questionnaire',
|
|
274
|
+
id: 'QuantityCalculation',
|
|
275
|
+
name: 'QuantityCalculation',
|
|
276
|
+
title: 'Quantity Calculation',
|
|
277
|
+
version: '0.1.0',
|
|
278
|
+
status: 'draft',
|
|
279
|
+
publisher: 'AEHRC CSIRO',
|
|
280
|
+
date: '2024-05-01',
|
|
281
|
+
url: 'https://smartforms.csiro.au/docs/components/quantity/calculation',
|
|
282
|
+
extension: [
|
|
283
|
+
{
|
|
284
|
+
url: 'http://hl7.org/fhir/StructureDefinition/variable',
|
|
285
|
+
valueExpression: {
|
|
286
|
+
name: 'durationInDays',
|
|
287
|
+
language: 'text/fhirpath',
|
|
288
|
+
expression: "item.where(linkId='duration-in-days').answer.value"
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
],
|
|
292
|
+
item: [
|
|
293
|
+
{
|
|
294
|
+
extension: [
|
|
295
|
+
{
|
|
296
|
+
url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-unit',
|
|
297
|
+
valueCoding: { system: 'http://unitsofmeasure.org', code: 'd', display: 'days' }
|
|
298
|
+
}
|
|
299
|
+
],
|
|
300
|
+
linkId: 'duration-in-days',
|
|
301
|
+
type: 'quantity',
|
|
302
|
+
repeats: false,
|
|
303
|
+
text: 'Duration in Days'
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
extension: [
|
|
307
|
+
{
|
|
308
|
+
url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-unit',
|
|
309
|
+
valueCoding: { system: 'http://unitsofmeasure.org', code: 'h', display: 'hours' }
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-calculatedExpression',
|
|
313
|
+
valueExpression: {
|
|
314
|
+
description: 'Duration In Hours',
|
|
315
|
+
language: 'text/fhirpath',
|
|
316
|
+
expression: '%durationInDays.value * 24'
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
],
|
|
320
|
+
linkId: 'duration-in-hours',
|
|
321
|
+
type: 'quantity',
|
|
322
|
+
repeats: false,
|
|
323
|
+
text: 'Duration in Hours'
|
|
42
324
|
}
|
|
43
325
|
]
|
|
44
326
|
};
|
|
@@ -17,7 +17,13 @@
|
|
|
17
17
|
|
|
18
18
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
19
19
|
import BuildFormWrapperForStorybook from '../storybookWrappers/BuildFormWrapperForStorybook';
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
qQuantityBasic,
|
|
22
|
+
qQuantityCalculation,
|
|
23
|
+
qQuantityUnitOption,
|
|
24
|
+
qrQuantityBasicResponse,
|
|
25
|
+
qrQuantityUnitOptionResponse
|
|
26
|
+
} from '../assets/questionnaires';
|
|
21
27
|
|
|
22
28
|
// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction#default-export
|
|
23
29
|
const meta = {
|
|
@@ -37,3 +43,29 @@ export const QuantityBasic: Story = {
|
|
|
37
43
|
questionnaire: qQuantityBasic
|
|
38
44
|
}
|
|
39
45
|
};
|
|
46
|
+
|
|
47
|
+
export const QuantityBasicResponse: Story = {
|
|
48
|
+
args: {
|
|
49
|
+
questionnaire: qQuantityBasic,
|
|
50
|
+
questionnaireResponse: qrQuantityBasicResponse
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const QuantityUnitOption: Story = {
|
|
55
|
+
args: {
|
|
56
|
+
questionnaire: qQuantityUnitOption
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const QuantityUnitOptionResponse: Story = {
|
|
61
|
+
args: {
|
|
62
|
+
questionnaire: qQuantityUnitOption,
|
|
63
|
+
questionnaireResponse: qrQuantityUnitOptionResponse
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export const QuantityCalculation: Story = {
|
|
68
|
+
args: {
|
|
69
|
+
questionnaire: qQuantityCalculation
|
|
70
|
+
}
|
|
71
|
+
};
|
|
@@ -323,7 +323,11 @@ function parseValueToAnswer(qItem: QuestionnaireItem, value: any): Questionnaire
|
|
|
323
323
|
}
|
|
324
324
|
}
|
|
325
325
|
|
|
326
|
-
if (typeof value === 'object') {
|
|
326
|
+
if (typeof value === 'object' && value.unit) {
|
|
327
|
+
return { valueQuantity: value };
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (typeof value === 'object' && value.system && value.code) {
|
|
327
331
|
return { valueCoding: value };
|
|
328
332
|
}
|
|
329
333
|
|
package/src/utils/itemControl.ts
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import type { Coding, Extension, QuestionnaireItem } from 'fhir/r4';
|
|
18
|
+
import type { Coding, Extension, QuestionnaireItem, QuestionnaireItemAnswerOption } from 'fhir/r4';
|
|
19
19
|
import type { RegexValidation } from '../interfaces/regex.interface';
|
|
20
20
|
import { structuredDataCapture } from 'fhir-sdc-helpers';
|
|
21
21
|
|
|
@@ -234,6 +234,24 @@ export function getTextDisplayPrompt(qItem: QuestionnaireItem): string {
|
|
|
234
234
|
return '';
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
+
/**
|
|
238
|
+
* Get Quantity unit for items with itemControlCode unit and has a unit childItem
|
|
239
|
+
*
|
|
240
|
+
* @author Sean Fong
|
|
241
|
+
*/
|
|
242
|
+
export function getQuantityUnit(qItem: QuestionnaireItem): QuestionnaireItemAnswerOption | null {
|
|
243
|
+
// Otherwise, check if the item has a unit extension
|
|
244
|
+
const itemControl = qItem.extension?.find(
|
|
245
|
+
(extension: Extension) =>
|
|
246
|
+
extension.url === 'http://hl7.org/fhir/StructureDefinition/questionnaire-unit'
|
|
247
|
+
);
|
|
248
|
+
if (itemControl && itemControl.valueCoding) {
|
|
249
|
+
return itemControl;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
|
|
237
255
|
/**
|
|
238
256
|
* Get decimal text display unit for items with itemControlCode unit and has a unit childItem
|
|
239
257
|
*
|