@aehrc/smart-forms-renderer 0.44.0 → 1.0.0-alpha.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.
- package/lib/components/FormComponents/CustomComponent.d.ts +9 -0
- package/lib/components/FormComponents/CustomComponent.js +31 -0
- package/lib/components/FormComponents/CustomComponent.js.map +1 -0
- package/lib/components/FormComponents/CustomComponentWrapper.d.ts +1 -0
- package/lib/components/FormComponents/CustomComponentWrapper.js +40 -0
- package/lib/components/FormComponents/CustomComponentWrapper.js.map +1 -0
- package/lib/components/FormComponents/ItemParts/ItemLabelText.js +4 -1
- package/lib/components/FormComponents/ItemParts/ItemLabelText.js.map +1 -1
- package/lib/components/FormComponents/ItemParts/ItemLabelWrapper.js +6 -5
- package/lib/components/FormComponents/ItemParts/ItemLabelWrapper.js.map +1 -1
- package/lib/components/FormComponents/ItemParts/index.d.ts +1 -0
- package/lib/components/FormComponents/ItemParts/index.js +18 -0
- package/lib/components/FormComponents/ItemParts/index.js.map +1 -0
- package/lib/components/FormComponents/OpenChoiceItems/OpenChoiceCheckboxAnswerValueSetItem.js +1 -1
- package/lib/components/FormComponents/OpenChoiceItems/OpenChoiceCheckboxAnswerValueSetItem.js.map +1 -1
- package/lib/components/FormComponents/SingleItem/SingleItemSwitcher.js +9 -0
- package/lib/components/FormComponents/SingleItem/SingleItemSwitcher.js.map +1 -1
- package/lib/components/FormComponents/StringItem/index.d.ts +1 -0
- package/lib/components/FormComponents/StringItem/index.js +18 -0
- package/lib/components/FormComponents/StringItem/index.js.map +1 -0
- package/lib/components/FormComponents/index.d.ts +2 -0
- package/lib/components/FormComponents/index.js +3 -0
- package/lib/components/FormComponents/index.js.map +1 -1
- package/lib/components/index.d.ts +2 -1
- package/lib/components/index.js +3 -1
- package/lib/components/index.js.map +1 -1
- package/lib/hooks/index.d.ts +4 -0
- package/lib/hooks/index.js +5 -0
- package/lib/hooks/index.js.map +1 -1
- package/lib/hooks/useBuildForm.d.ts +5 -1
- package/lib/hooks/useBuildForm.js +10 -2
- package/lib/hooks/useBuildForm.js.map +1 -1
- package/lib/index.d.ts +4 -4
- package/lib/index.js +3 -3
- package/lib/index.js.map +1 -1
- package/lib/interfaces/customComponent.d.ts +9 -0
- package/lib/interfaces/customComponent.interface.d.ts +9 -0
- package/lib/interfaces/customComponent.interface.js +18 -0
- package/lib/interfaces/customComponent.interface.js.map +1 -0
- package/lib/interfaces/customComponent.js +18 -0
- package/lib/interfaces/customComponent.js.map +1 -0
- package/lib/interfaces/index.d.ts +1 -0
- package/lib/stores/questionnaireStore.d.ts +6 -2
- package/lib/stores/questionnaireStore.js +5 -2
- package/lib/stores/questionnaireStore.js.map +1 -1
- package/lib/stores/rendererStylingStore.d.ts +22 -0
- package/lib/stores/rendererStylingStore.js +34 -0
- package/lib/stores/rendererStylingStore.js.map +1 -0
- package/lib/stores/stylingStore.d.ts +23 -0
- package/lib/stores/stylingStore.js +27 -0
- package/lib/stores/stylingStore.js.map +1 -0
- package/lib/utils/customComponent.d.ts +9 -0
- package/lib/utils/customComponent.js +18 -0
- package/lib/utils/customComponent.js.map +1 -0
- package/lib/utils/enableWhen.js +12 -5
- package/lib/utils/enableWhen.js.map +1 -1
- package/lib/utils/index.d.ts +1 -0
- package/lib/utils/index.js +1 -0
- package/lib/utils/index.js.map +1 -1
- package/lib/utils/manageForm.d.ts +3 -1
- package/lib/utils/manageForm.js +3 -2
- package/lib/utils/manageForm.js.map +1 -1
- package/lib/utils/openChoice.js +20 -2
- package/lib/utils/openChoice.js.map +1 -1
- package/package.json +3 -1
- package/src/components/FormComponents/ItemParts/ItemLabelText.tsx +9 -1
- package/src/components/FormComponents/ItemParts/ItemLabelWrapper.tsx +12 -10
- package/src/components/FormComponents/ItemParts/index.ts +18 -0
- package/src/components/FormComponents/OpenChoiceItems/OpenChoiceCheckboxAnswerValueSetItem.tsx +1 -1
- package/src/components/FormComponents/SingleItem/SingleItemSwitcher.tsx +20 -0
- package/src/components/FormComponents/StringItem/index.ts +18 -0
- package/src/components/FormComponents/index.ts +4 -0
- package/src/components/index.ts +6 -1
- package/src/hooks/index.ts +6 -0
- package/src/hooks/useBuildForm.ts +16 -3
- package/src/index.ts +22 -3
- package/src/interfaces/customComponent.interface.ts +27 -0
- package/src/interfaces/index.ts +1 -0
- package/src/stores/questionnaireStore.ts +11 -3
- package/src/stores/rendererStylingStore.ts +67 -0
- package/src/stories/assets/questionnaires/QBehaviorOther.ts +92 -0
- package/src/stories/sdc/BehaviorOther.stories.tsx +7 -0
- package/src/utils/enableWhen.ts +15 -5
- package/src/utils/index.ts +2 -0
- package/src/utils/manageForm.ts +12 -2
- package/src/utils/openChoice.ts +10 -4
|
@@ -21,3 +21,7 @@ export { RepeatGroup } from './RepeatGroup';
|
|
|
21
21
|
export { GroupTable } from './Tables';
|
|
22
22
|
export { GridGroup } from './GridGroup';
|
|
23
23
|
export { parseFhirDateToDisplayDate } from './DateTimeItems';
|
|
24
|
+
export { ItemFieldGrid } from './ItemParts';
|
|
25
|
+
|
|
26
|
+
// item type components
|
|
27
|
+
export { StringField } from './StringItem';
|
package/src/components/index.ts
CHANGED
|
@@ -23,5 +23,10 @@ export {
|
|
|
23
23
|
RepeatGroup,
|
|
24
24
|
GroupTable,
|
|
25
25
|
GridGroup,
|
|
26
|
-
parseFhirDateToDisplayDate
|
|
26
|
+
parseFhirDateToDisplayDate,
|
|
27
|
+
ItemFieldGrid,
|
|
28
|
+
StringField
|
|
27
29
|
} from './FormComponents';
|
|
30
|
+
|
|
31
|
+
// Styled MUI components
|
|
32
|
+
export { FullWidthFormComponentBox } from './Box.styles';
|
package/src/hooks/index.ts
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
1
|
export { default as useHidden } from './useHidden';
|
|
2
|
+
export { default as useReadOnly } from './useReadOnly';
|
|
2
3
|
export { default as useBuildForm } from './useBuildForm';
|
|
3
4
|
export { default as useRendererQueryClient } from './useRendererQueryClient';
|
|
5
|
+
export { default as useRenderingExtensions } from './useRenderingExtensions';
|
|
6
|
+
export { default as useValidationFeedback } from './useValidationFeedback';
|
|
7
|
+
|
|
8
|
+
// CalculatedExpression hooks
|
|
9
|
+
export { default as useStringCalculatedExpression } from './useStringCalculatedExpression';
|
|
@@ -15,9 +15,10 @@
|
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import { useLayoutEffect, useState } from 'react';
|
|
18
|
+
import { ComponentType, useLayoutEffect, useState } from 'react';
|
|
19
19
|
import { buildForm } from '../utils';
|
|
20
20
|
import type { Questionnaire, QuestionnaireResponse } from 'fhir/r4';
|
|
21
|
+
import { RendererStyling, useRendererStylingStore } from '../stores/rendererStylingStore';
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* React hook wrapping around the buildForm() function to build a form from a questionnaire and an optional QuestionnaireResponse.
|
|
@@ -28,6 +29,8 @@ import type { Questionnaire, QuestionnaireResponse } from 'fhir/r4';
|
|
|
28
29
|
* @param readOnly - Applies read-only mode to all items in the form view
|
|
29
30
|
* @param terminologyServerUrl - Terminology server url to fetch terminology. If not provided, the default terminology server will be used. (optional)
|
|
30
31
|
* @param additionalVariables - Additional key-value pair of SDC variables `Record<name, variable extension>` for testing (optional)
|
|
32
|
+
* @param rendererStyling - Renderer styling to be applied to the form. See docs for styling options. (optional)
|
|
33
|
+
* @param customComponents - FIXME add comment
|
|
31
34
|
*
|
|
32
35
|
* @author Sean Fong
|
|
33
36
|
*/
|
|
@@ -36,17 +39,27 @@ function useBuildForm(
|
|
|
36
39
|
questionnaireResponse?: QuestionnaireResponse,
|
|
37
40
|
readOnly?: boolean,
|
|
38
41
|
terminologyServerUrl?: string,
|
|
39
|
-
additionalVariables?: Record<string, object
|
|
42
|
+
additionalVariables?: Record<string, object>,
|
|
43
|
+
rendererStyling?: RendererStyling,
|
|
44
|
+
customComponents?: Record<string, ComponentType<any>>
|
|
40
45
|
) {
|
|
41
46
|
const [isBuilding, setIsBuilding] = useState(true);
|
|
42
47
|
|
|
48
|
+
const setRendererStyling = useRendererStylingStore.use.setRendererStyling();
|
|
49
|
+
|
|
43
50
|
useLayoutEffect(() => {
|
|
51
|
+
// Set optional renderer styling
|
|
52
|
+
if (rendererStyling) {
|
|
53
|
+
setRendererStyling(rendererStyling);
|
|
54
|
+
}
|
|
55
|
+
|
|
44
56
|
buildForm(
|
|
45
57
|
questionnaire,
|
|
46
58
|
questionnaireResponse,
|
|
47
59
|
readOnly,
|
|
48
60
|
terminologyServerUrl,
|
|
49
|
-
additionalVariables
|
|
61
|
+
additionalVariables,
|
|
62
|
+
customComponents
|
|
50
63
|
).then(() => {
|
|
51
64
|
setIsBuilding(false);
|
|
52
65
|
});
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
// interface exports
|
|
2
|
-
export type {
|
|
2
|
+
export type {
|
|
3
|
+
Tab,
|
|
4
|
+
Tabs,
|
|
5
|
+
Variables,
|
|
6
|
+
VariableXFhirQuery,
|
|
7
|
+
LaunchContext,
|
|
8
|
+
CustomComponentProps
|
|
9
|
+
} from './interfaces';
|
|
3
10
|
|
|
4
11
|
// component exports
|
|
5
12
|
export type { SmartFormsRendererProps } from './components';
|
|
@@ -11,7 +18,10 @@ export {
|
|
|
11
18
|
RepeatGroup,
|
|
12
19
|
GroupTable,
|
|
13
20
|
GridGroup,
|
|
14
|
-
parseFhirDateToDisplayDate
|
|
21
|
+
parseFhirDateToDisplayDate,
|
|
22
|
+
ItemFieldGrid,
|
|
23
|
+
StringField,
|
|
24
|
+
FullWidthFormComponentBox
|
|
15
25
|
} from './components';
|
|
16
26
|
|
|
17
27
|
// state management store exports
|
|
@@ -33,7 +43,15 @@ export {
|
|
|
33
43
|
} from './stores';
|
|
34
44
|
|
|
35
45
|
// hooks exports
|
|
36
|
-
export {
|
|
46
|
+
export {
|
|
47
|
+
useHidden,
|
|
48
|
+
useReadOnly,
|
|
49
|
+
useBuildForm,
|
|
50
|
+
useRendererQueryClient,
|
|
51
|
+
useRenderingExtensions,
|
|
52
|
+
useValidationFeedback,
|
|
53
|
+
useStringCalculatedExpression
|
|
54
|
+
} from './hooks';
|
|
37
55
|
|
|
38
56
|
// utils exports
|
|
39
57
|
export type { ItemToRepopulate } from './utils';
|
|
@@ -43,6 +61,7 @@ export {
|
|
|
43
61
|
getResponse,
|
|
44
62
|
removeEmptyAnswersFromResponse,
|
|
45
63
|
removeInternalIdsFromResponse,
|
|
64
|
+
createEmptyQrItem,
|
|
46
65
|
isSpecificItemControl,
|
|
47
66
|
isRepeatItemAndNotCheckbox,
|
|
48
67
|
initialiseQuestionnaireResponse,
|
|
@@ -0,0 +1,27 @@
|
|
|
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 { QuestionnaireItem, type QuestionnaireResponseItem } from 'fhir/r4';
|
|
19
|
+
|
|
20
|
+
export interface CustomComponentProps {
|
|
21
|
+
qItem: QuestionnaireItem;
|
|
22
|
+
qrItem: QuestionnaireResponseItem | null;
|
|
23
|
+
isRepeated: boolean;
|
|
24
|
+
isTabled: boolean;
|
|
25
|
+
parentIsReadOnly?: boolean;
|
|
26
|
+
onQrItemChange: (qrItem: QuestionnaireResponseItem) => unknown;
|
|
27
|
+
}
|
package/src/interfaces/index.ts
CHANGED
|
@@ -19,3 +19,4 @@ export type { Tab, Tabs } from './tab.interface';
|
|
|
19
19
|
export type { Variables, VariableXFhirQuery } from './variables.interface';
|
|
20
20
|
export type { LaunchContext } from './populate.interface';
|
|
21
21
|
export type { EnableWhenItems, EnableWhenExpressions } from './enableWhen.interface';
|
|
22
|
+
export type { CustomComponentProps } from './customComponent.interface';
|
|
@@ -48,6 +48,8 @@ import { questionnaireResponseStore } from './questionnaireResponseStore';
|
|
|
48
48
|
import { createQuestionnaireResponseItemMap } from '../utils/questionnaireResponseStoreUtils/updatableResponseItems';
|
|
49
49
|
import { insertCompleteAnswerOptionsIntoQuestionnaire } from '../utils/questionnaireStoreUtils/insertAnswerOptions';
|
|
50
50
|
import type { InitialExpression } from '../interfaces/initialExpression.interface';
|
|
51
|
+
import React from 'react';
|
|
52
|
+
import { CustomComponentProps } from '../interfaces/customComponent.interface';
|
|
51
53
|
|
|
52
54
|
/**
|
|
53
55
|
* QuestionnaireStore properties and methods
|
|
@@ -116,6 +118,7 @@ export interface QuestionnaireStoreType {
|
|
|
116
118
|
cachedValueSetCodings: Record<string, Coding[]>;
|
|
117
119
|
fhirPathContext: Record<string, any>;
|
|
118
120
|
populatedContext: Record<string, any>;
|
|
121
|
+
customComponents: Record<string, React.ComponentType<CustomComponentProps>>;
|
|
119
122
|
focusedLinkId: string;
|
|
120
123
|
readOnly: boolean;
|
|
121
124
|
buildSourceQuestionnaire: (
|
|
@@ -123,7 +126,8 @@ export interface QuestionnaireStoreType {
|
|
|
123
126
|
questionnaireResponse?: QuestionnaireResponse,
|
|
124
127
|
additionalVariables?: Record<string, object>,
|
|
125
128
|
terminologyServerUrl?: string,
|
|
126
|
-
readOnly?: boolean
|
|
129
|
+
readOnly?: boolean,
|
|
130
|
+
customComponents?: Record<string, React.ComponentType<any>>
|
|
127
131
|
) => Promise<void>;
|
|
128
132
|
destroySourceQuestionnaire: () => void;
|
|
129
133
|
switchTab: (newTabIndex: number) => void;
|
|
@@ -182,6 +186,7 @@ export const questionnaireStore = createStore<QuestionnaireStoreType>()((set, ge
|
|
|
182
186
|
cachedValueSetCodings: {},
|
|
183
187
|
fhirPathContext: {},
|
|
184
188
|
populatedContext: {},
|
|
189
|
+
customComponents: {},
|
|
185
190
|
focusedLinkId: '',
|
|
186
191
|
readOnly: false,
|
|
187
192
|
buildSourceQuestionnaire: async (
|
|
@@ -189,7 +194,8 @@ export const questionnaireStore = createStore<QuestionnaireStoreType>()((set, ge
|
|
|
189
194
|
questionnaireResponse = structuredClone(emptyResponse),
|
|
190
195
|
additionalVariables = {},
|
|
191
196
|
terminologyServerUrl = terminologyServerStore.getState().url,
|
|
192
|
-
readOnly = false
|
|
197
|
+
readOnly = false,
|
|
198
|
+
customComponents = {}
|
|
193
199
|
) => {
|
|
194
200
|
const questionnaireModel = await createQuestionnaireModel(
|
|
195
201
|
questionnaire,
|
|
@@ -242,6 +248,7 @@ export const questionnaireStore = createStore<QuestionnaireStoreType>()((set, ge
|
|
|
242
248
|
processedValueSetCodings: questionnaireModel.processedValueSetCodings,
|
|
243
249
|
processedValueSetUrls: questionnaireModel.processedValueSetUrls,
|
|
244
250
|
fhirPathContext: updatedFhirPathContext,
|
|
251
|
+
customComponents: customComponents,
|
|
245
252
|
readOnly: readOnly
|
|
246
253
|
});
|
|
247
254
|
},
|
|
@@ -264,7 +271,8 @@ export const questionnaireStore = createStore<QuestionnaireStoreType>()((set, ge
|
|
|
264
271
|
answerExpressions: {},
|
|
265
272
|
processedValueSetCodings: {},
|
|
266
273
|
processedValueSetUrls: {},
|
|
267
|
-
fhirPathContext: {}
|
|
274
|
+
fhirPathContext: {},
|
|
275
|
+
customComponents: {}
|
|
268
276
|
}),
|
|
269
277
|
switchTab: (newTabIndex: number) => set(() => ({ currentTabIndex: newTabIndex })),
|
|
270
278
|
switchPage: (newPageIndex: number) => set(() => ({ currentPageIndex: newPageIndex })),
|
|
@@ -0,0 +1,67 @@
|
|
|
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 { createStore } from 'zustand/vanilla';
|
|
19
|
+
import { createSelectors } from './selector';
|
|
20
|
+
|
|
21
|
+
export interface RendererStyling {
|
|
22
|
+
itemLabelFontWeight?:
|
|
23
|
+
| '100'
|
|
24
|
+
| '200'
|
|
25
|
+
| '300'
|
|
26
|
+
| '400'
|
|
27
|
+
| '500'
|
|
28
|
+
| '600'
|
|
29
|
+
| '700'
|
|
30
|
+
| '800'
|
|
31
|
+
| '900'
|
|
32
|
+
| 'default';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* RendererStylingStore properties and methods
|
|
37
|
+
*
|
|
38
|
+
* @author Sean Fong
|
|
39
|
+
*/
|
|
40
|
+
export interface RendererStylingStoreType {
|
|
41
|
+
itemLabelFontWeight:
|
|
42
|
+
| '100'
|
|
43
|
+
| '200'
|
|
44
|
+
| '300'
|
|
45
|
+
| '400'
|
|
46
|
+
| '500'
|
|
47
|
+
| '600'
|
|
48
|
+
| '700'
|
|
49
|
+
| '800'
|
|
50
|
+
| '900'
|
|
51
|
+
| 'default';
|
|
52
|
+
setRendererStyling: (params: RendererStyling) => void;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @author Sean Fong
|
|
57
|
+
*/
|
|
58
|
+
export const rendererStylingStore = createStore<RendererStylingStoreType>()((set) => ({
|
|
59
|
+
itemLabelFontWeight: 'default',
|
|
60
|
+
setRendererStyling: (params: RendererStyling) => {
|
|
61
|
+
set(() => ({
|
|
62
|
+
itemLabelFontWeight: params.itemLabelFontWeight ?? 'default'
|
|
63
|
+
}));
|
|
64
|
+
}
|
|
65
|
+
}));
|
|
66
|
+
|
|
67
|
+
export const useRendererStylingStore = createSelectors(rendererStylingStore);
|
|
@@ -315,6 +315,98 @@ export const qEnableWhen: Questionnaire = {
|
|
|
315
315
|
]
|
|
316
316
|
};
|
|
317
317
|
|
|
318
|
+
export const qEnableWhenMultiCheckbox: Questionnaire = {
|
|
319
|
+
resourceType: 'Questionnaire',
|
|
320
|
+
id: 'EnableWhenMultiCheckbox',
|
|
321
|
+
name: 'EnableWhenMultiCheckbox',
|
|
322
|
+
title: 'EnableWhen Multi-select Checkbox',
|
|
323
|
+
version: '0.1.0',
|
|
324
|
+
status: 'draft',
|
|
325
|
+
date: '2024-05-01',
|
|
326
|
+
url: 'https://smartforms.csiro.au/docs/behavior/other/enable-when-multi-checkbox',
|
|
327
|
+
item: [
|
|
328
|
+
{
|
|
329
|
+
extension: [
|
|
330
|
+
{
|
|
331
|
+
url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl',
|
|
332
|
+
valueCodeableConcept: {
|
|
333
|
+
coding: [
|
|
334
|
+
{
|
|
335
|
+
system: 'http://hl7.org/fhir/questionnaire-item-control',
|
|
336
|
+
code: 'check-box'
|
|
337
|
+
}
|
|
338
|
+
]
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-openLabel',
|
|
343
|
+
valueString: 'Other, please specify'
|
|
344
|
+
}
|
|
345
|
+
],
|
|
346
|
+
linkId: 'select-conditions-list',
|
|
347
|
+
text: 'Select one or more conditions',
|
|
348
|
+
type: 'open-choice',
|
|
349
|
+
repeats: true,
|
|
350
|
+
answerOption: [
|
|
351
|
+
{
|
|
352
|
+
valueString: 'Condition A (Displays Clinical guidance: Condition A question)'
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
valueString: 'Condition B (Displays Clinical guidance: Condition B question)'
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
valueString: 'Condition C (Displays Clinical guidance: Condition C question)'
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
valueString: 'Condition D'
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
valueString: 'Condition E'
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
valueString: 'Condition F'
|
|
368
|
+
}
|
|
369
|
+
]
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
linkId: 'clinical-guidance-a',
|
|
373
|
+
text: 'Clinical guidance: Condition A',
|
|
374
|
+
type: 'display',
|
|
375
|
+
enableWhen: [
|
|
376
|
+
{
|
|
377
|
+
question: 'select-conditions-list',
|
|
378
|
+
operator: '=',
|
|
379
|
+
answerString: 'Condition A (Displays Clinical guidance: Condition A question)'
|
|
380
|
+
}
|
|
381
|
+
]
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
linkId: 'clinical-guidance-b',
|
|
385
|
+
text: 'Clinical guidance: Condition B',
|
|
386
|
+
type: 'display',
|
|
387
|
+
enableWhen: [
|
|
388
|
+
{
|
|
389
|
+
question: 'select-conditions-list',
|
|
390
|
+
operator: '=',
|
|
391
|
+
answerString: 'Condition B (Displays Clinical guidance: Condition B question)'
|
|
392
|
+
}
|
|
393
|
+
]
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
linkId: 'clinical-guidance-c',
|
|
397
|
+
text: 'Clinical guidance: Condition C',
|
|
398
|
+
type: 'display',
|
|
399
|
+
enableWhen: [
|
|
400
|
+
{
|
|
401
|
+
question: 'select-conditions-list',
|
|
402
|
+
operator: '=',
|
|
403
|
+
answerString: 'Condition C (Displays Clinical guidance: Condition C question)'
|
|
404
|
+
}
|
|
405
|
+
]
|
|
406
|
+
}
|
|
407
|
+
]
|
|
408
|
+
};
|
|
409
|
+
|
|
318
410
|
export const qEnableBehaviorAll: Questionnaire = {
|
|
319
411
|
resourceType: 'Questionnaire',
|
|
320
412
|
id: 'EnableBehaviorAll',
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
qEnableWhen,
|
|
24
24
|
qEnableWhenExpressionSimple,
|
|
25
25
|
qEnableWhenExpressionTabs,
|
|
26
|
+
qEnableWhenMultiCheckbox,
|
|
26
27
|
qInitialRepeats,
|
|
27
28
|
qInitialSingle,
|
|
28
29
|
qText
|
|
@@ -59,6 +60,12 @@ export const EnableWhen: Story = {
|
|
|
59
60
|
}
|
|
60
61
|
};
|
|
61
62
|
|
|
63
|
+
export const EnableWhenMultiCheckbox: Story = {
|
|
64
|
+
args: {
|
|
65
|
+
questionnaire: qEnableWhenMultiCheckbox
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
62
69
|
export const EnableBehaviorAll: Story = {
|
|
63
70
|
args: {
|
|
64
71
|
questionnaire: qEnableBehaviorAll
|
package/src/utils/enableWhen.ts
CHANGED
|
@@ -339,19 +339,29 @@ export function checkItemIsEnabledSingle(
|
|
|
339
339
|
): boolean {
|
|
340
340
|
const checkedIsEnabledItems: boolean[] = [];
|
|
341
341
|
|
|
342
|
+
// Check if linked item satisfies enableWhen condition
|
|
342
343
|
for (const linkedItem of enableWhenItemProperties.linked) {
|
|
344
|
+
let isEnabledForThisLinkedItem = false;
|
|
345
|
+
|
|
346
|
+
// Linked item has answers
|
|
343
347
|
if (linkedItem.answer && linkedItem.answer.length > 0) {
|
|
348
|
+
// Check if linked answer within item satisfies enableWhen condition
|
|
349
|
+
// Exit early once a linked answer is found to satisfy the condition
|
|
344
350
|
for (const answer of linkedItem.answer) {
|
|
345
|
-
const
|
|
351
|
+
const isEnabledForThisLinkedAnswer = isEnabledAnswerTypeSwitcher(
|
|
346
352
|
linkedItem.enableWhen,
|
|
347
353
|
answer
|
|
348
354
|
);
|
|
349
355
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
356
|
+
if (isEnabledForThisLinkedAnswer) {
|
|
357
|
+
isEnabledForThisLinkedItem = true;
|
|
358
|
+
break;
|
|
359
|
+
}
|
|
354
360
|
}
|
|
361
|
+
|
|
362
|
+
// Push result of the linked item to the checkedIsEnabledItems array
|
|
363
|
+
checkedIsEnabledItems.push(isEnabledForThisLinkedItem);
|
|
364
|
+
|
|
355
365
|
continue;
|
|
356
366
|
}
|
|
357
367
|
|
package/src/utils/index.ts
CHANGED
|
@@ -23,6 +23,8 @@ export {
|
|
|
23
23
|
removeInternalIdsFromResponse
|
|
24
24
|
} from './manageForm';
|
|
25
25
|
export { initialiseQuestionnaireResponse } from './initialise';
|
|
26
|
+
export { createEmptyQrItem } from './qrItem';
|
|
27
|
+
|
|
26
28
|
export { isSpecificItemControl } from './itemControl';
|
|
27
29
|
export { isRepeatItemAndNotCheckbox } from './qItem';
|
|
28
30
|
export type { ItemToRepopulate } from './repopulateItems';
|
package/src/utils/manageForm.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { readEncounter, readPatient, readUser } from '../api/smartClient';
|
|
|
11
11
|
import type Client from 'fhirclient/lib/Client';
|
|
12
12
|
import { updateQuestionnaireResponse } from './genericRecursive';
|
|
13
13
|
import { removeInternalRepeatIdsRecursive } from './removeRepeatId';
|
|
14
|
+
import { ComponentType } from 'react';
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Build the form with an initial Questionnaire and an optional filled QuestionnaireResponse.
|
|
@@ -22,6 +23,7 @@ import { removeInternalRepeatIdsRecursive } from './removeRepeatId';
|
|
|
22
23
|
* @param readOnly - Applies read-only mode to all items in the form view
|
|
23
24
|
* @param terminologyServerUrl - Terminology server url to fetch terminology. If not provided, the default terminology server will be used. (optional)
|
|
24
25
|
* @param additionalVariables - Additional key-value pair of SDC variables `Record<name, variable extension>` for testing (optional)
|
|
26
|
+
* @param customComponents - FIXME add comment
|
|
25
27
|
*
|
|
26
28
|
* @author Sean Fong
|
|
27
29
|
*/
|
|
@@ -30,7 +32,8 @@ export async function buildForm(
|
|
|
30
32
|
questionnaireResponse?: QuestionnaireResponse,
|
|
31
33
|
readOnly?: boolean,
|
|
32
34
|
terminologyServerUrl?: string,
|
|
33
|
-
additionalVariables?: Record<string, object
|
|
35
|
+
additionalVariables?: Record<string, object>,
|
|
36
|
+
customComponents?: Record<string, ComponentType<any>>
|
|
34
37
|
): Promise<void> {
|
|
35
38
|
// Reset terminology server
|
|
36
39
|
if (terminologyServerUrl) {
|
|
@@ -42,7 +45,14 @@ export async function buildForm(
|
|
|
42
45
|
// QR is set to undefined here to prevent it from being initialised twice. This is defined like that for backward compatibility purposes.
|
|
43
46
|
await questionnaireStore
|
|
44
47
|
.getState()
|
|
45
|
-
.buildSourceQuestionnaire(
|
|
48
|
+
.buildSourceQuestionnaire(
|
|
49
|
+
questionnaire,
|
|
50
|
+
undefined,
|
|
51
|
+
additionalVariables,
|
|
52
|
+
terminologyServerUrl,
|
|
53
|
+
undefined,
|
|
54
|
+
customComponents
|
|
55
|
+
);
|
|
46
56
|
|
|
47
57
|
const initialisedQuestionnaireResponse = initialiseQuestionnaireResponse(
|
|
48
58
|
questionnaire,
|
package/src/utils/openChoice.ts
CHANGED
|
@@ -24,6 +24,7 @@ import type {
|
|
|
24
24
|
import { OpenChoiceItemControl } from '../interfaces/choice.enum';
|
|
25
25
|
import { isSpecificItemControl } from './itemControl';
|
|
26
26
|
import isEqual from 'lodash.isequal';
|
|
27
|
+
import differenceWith from 'lodash.differencewith';
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
30
|
* Update open choice answer based on open label value
|
|
@@ -121,10 +122,15 @@ export function getOldOpenLabelAnswer(
|
|
|
121
122
|
answers: QuestionnaireResponseItemAnswer[],
|
|
122
123
|
options: QuestionnaireItemAnswerOption[]
|
|
123
124
|
): QuestionnaireResponseItemAnswer | null {
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
125
|
+
const answersWithoutId = answers.map((answer) => {
|
|
126
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
127
|
+
const { id, ...rest } = answer;
|
|
128
|
+
return rest as QuestionnaireResponseItemAnswer;
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
const outliers = differenceWith(answersWithoutId, options, isEqual);
|
|
132
|
+
|
|
133
|
+
return outliers?.[0] ?? null;
|
|
128
134
|
}
|
|
129
135
|
|
|
130
136
|
/**
|