@aehrc/smart-forms-renderer 0.7.0 → 0.7.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/README.md +5 -5
- package/lib/components/FormComponents/ChoiceItems/ChoiceAutocompleteItem.js +1 -1
- package/lib/components/FormComponents/ChoiceItems/ChoiceAutocompleteItem.js.map +1 -1
- package/lib/components/FormComponents/DecimalItem/DecimalItem.js +3 -2
- package/lib/components/FormComponents/DecimalItem/DecimalItem.js.map +1 -1
- package/lib/components/FormComponents/GridGroup/GridGroup.js +2 -2
- package/lib/components/FormComponents/GridGroup/GridGroup.js.map +1 -1
- package/lib/components/FormComponents/GridGroup/GridRow.js +2 -2
- package/lib/components/FormComponents/GridGroup/GridRow.js.map +1 -1
- package/lib/components/FormComponents/GroupItem/GroupChildItemSwitcher.d.ts +10 -0
- package/lib/components/FormComponents/GroupItem/GroupChildItemSwitcher.js +73 -0
- package/lib/components/FormComponents/GroupItem/GroupChildItemSwitcher.js.map +1 -0
- package/lib/components/FormComponents/GroupItem/GroupItem.js +3 -3
- package/lib/components/FormComponents/GroupItem/GroupItem.js.map +1 -1
- package/lib/components/FormComponents/GroupItem/ItemSwitcher.d.ts +10 -0
- package/lib/components/FormComponents/GroupItem/ItemSwitcher.js +81 -0
- package/lib/components/FormComponents/GroupItem/ItemSwitcher.js.map +1 -0
- package/lib/components/FormComponents/GroupItem/TopLevelGroupItemSwitcher.d.ts +10 -0
- package/lib/components/FormComponents/GroupItem/TopLevelGroupItemSwitcher.js +45 -0
- package/lib/components/FormComponents/GroupItem/TopLevelGroupItemSwitcher.js.map +1 -0
- package/lib/components/FormComponents/IntegerItem/IntegerItem.js +3 -2
- package/lib/components/FormComponents/IntegerItem/IntegerItem.js.map +1 -1
- package/lib/components/FormComponents/OpenChoiceItems/OpenChoiceAutocompleteItem.js +2 -2
- package/lib/components/FormComponents/OpenChoiceItems/OpenChoiceAutocompleteItem.js.map +1 -1
- package/lib/components/FormComponents/RepeatGroup/RepeatGroup.js +4 -3
- package/lib/components/FormComponents/RepeatGroup/RepeatGroup.js.map +1 -1
- package/lib/components/FormComponents/RepeatGroup/index.d.ts +1 -0
- package/lib/{theme/overrides/Typography.js → components/FormComponents/RepeatGroup/index.js} +2 -15
- package/lib/components/FormComponents/RepeatGroup/index.js.map +1 -0
- package/lib/components/FormComponents/RepeatItem/RepeatItem.js +1 -1
- package/lib/components/FormComponents/RepeatItem/RepeatItem.js.map +1 -1
- package/lib/components/FormComponents/SingleItem/index.d.ts +1 -0
- package/lib/components/FormComponents/SingleItem/index.js +18 -0
- package/lib/components/FormComponents/SingleItem/index.js.map +1 -0
- package/lib/components/FormComponents/StringItem/StringItem.js +3 -2
- package/lib/components/FormComponents/StringItem/StringItem.js.map +1 -1
- package/lib/components/FormComponents/Tables/QItemGroupTable.js +7 -5
- package/lib/components/FormComponents/Tables/QItemGroupTable.js.map +1 -1
- package/lib/components/FormComponents/Tables/QItemGroupTableRow.js +2 -2
- package/lib/components/FormComponents/Tables/QItemGroupTableRow.js.map +1 -1
- package/lib/components/FormComponents/Tables/index.d.ts +1 -0
- package/lib/components/FormComponents/Tables/index.js +18 -0
- package/lib/components/FormComponents/Tables/index.js.map +1 -0
- package/lib/components/FormComponents/TextItem/TextItem.js +3 -2
- package/lib/components/FormComponents/TextItem/TextItem.js.map +1 -1
- package/lib/components/FormComponents/index.d.ts +3 -0
- package/lib/components/FormComponents/index.js +20 -0
- package/lib/components/FormComponents/index.js.map +1 -0
- package/lib/components/Renderer/BaseRenderer.js +26 -14
- package/lib/components/Renderer/BaseRenderer.js.map +1 -1
- package/lib/components/Renderer/FormBodyCollapsible.js +2 -2
- package/lib/components/Renderer/FormBodyCollapsible.js.map +1 -1
- package/lib/components/Renderer/FormBodyTabbed.js +2 -2
- package/lib/components/Renderer/FormBodyTabbed.js.map +1 -1
- package/lib/components/Renderer/FormTopLevelItem.d.ts +4 -4
- package/lib/components/Renderer/FormTopLevelItem.js +14 -1
- package/lib/components/Renderer/FormTopLevelItem.js.map +1 -1
- package/lib/components/index.d.ts +1 -0
- package/lib/components/index.js +1 -0
- package/lib/components/index.js.map +1 -1
- package/lib/hooks/useDecimalUpdateFromProp.d.ts +2 -0
- package/lib/{utils/isHidden.js → hooks/useDecimalUpdateFromProp.js} +12 -13
- package/lib/hooks/useDecimalUpdateFromProp.js.map +1 -0
- package/lib/hooks/useInitialiseRepeatAnswers.d.ts +2 -2
- package/lib/hooks/useInitialiseRepeatAnswers.js +21 -15
- package/lib/hooks/useInitialiseRepeatAnswers.js.map +1 -1
- package/lib/hooks/useInitialiseRepeatGroups.d.ts +2 -1
- package/lib/hooks/useInitialiseRepeatGroups.js +21 -15
- package/lib/hooks/useInitialiseRepeatGroups.js.map +1 -1
- package/lib/hooks/useIntegerUpdateFromProp.d.ts +2 -0
- package/lib/hooks/useIntegerUpdateFromProp.js +29 -0
- package/lib/hooks/useIntegerUpdateFromProp.js.map +1 -0
- package/lib/hooks/useNumberInput.d.ts +3 -0
- package/lib/hooks/useNumberInput.js +31 -0
- package/lib/hooks/useNumberInput.js.map +1 -0
- package/lib/hooks/useNumberUpdateFromProp.d.ts +2 -0
- package/lib/hooks/useNumberUpdateFromProp.js +29 -0
- package/lib/hooks/useNumberUpdateFromProp.js.map +1 -0
- package/lib/hooks/useRepeatItemState.d.ts +5 -0
- package/lib/hooks/useRepeatItemState.js +35 -0
- package/lib/hooks/useRepeatItemState.js.map +1 -0
- package/lib/hooks/useRepeatItemUpdateFromProp.d.ts +2 -0
- package/lib/hooks/useRepeatItemUpdateFromProp.js +29 -0
- package/lib/hooks/useRepeatItemUpdateFromProp.js.map +1 -0
- package/lib/hooks/useStringField.d.ts +2 -0
- package/lib/hooks/useStringField.js +29 -0
- package/lib/hooks/useStringField.js.map +1 -0
- package/lib/hooks/useStringInput.d.ts +3 -0
- package/lib/hooks/useStringInput.js +31 -0
- package/lib/hooks/useStringInput.js.map +1 -0
- package/lib/hooks/useStringUpdateFromProp.d.ts +2 -0
- package/lib/hooks/useStringUpdateFromProp.js +29 -0
- package/lib/hooks/useStringUpdateFromProp.js.map +1 -0
- package/lib/index.d.ts +16 -0
- package/lib/index.js +27 -0
- package/lib/index.js.map +1 -1
- package/lib/interfaces/questionnaireStore.interface.d.ts +1 -0
- package/lib/stores/useQuestionnaireResponseStore.d.ts +1 -1
- package/lib/stores/useQuestionnaireResponseStore.js +15 -10
- package/lib/stores/useQuestionnaireResponseStore.js.map +1 -1
- package/lib/stores/useQuestionnaireStore.d.ts +2 -1
- package/lib/stores/useQuestionnaireStore.js +5 -2
- package/lib/stores/useQuestionnaireStore.js.map +1 -1
- package/lib/theme/overrides/Overrides.js +1 -2
- package/lib/theme/overrides/Overrides.js.map +1 -1
- package/lib/utils/calculatedExpression.js +2 -2
- package/lib/utils/calculatedExpression.js.map +1 -1
- package/lib/utils/formChanges.d.ts +18 -0
- package/lib/utils/formChanges.js +91 -0
- package/lib/utils/formChanges.js.map +1 -0
- package/lib/utils/formChangesOld.d.ts +18 -0
- package/lib/utils/formChangesOld.js +91 -0
- package/lib/utils/formChangesOld.js.map +1 -0
- package/lib/utils/index.d.ts +1 -0
- package/lib/utils/index.js +18 -0
- package/lib/utils/index.js.map +1 -0
- package/lib/utils/mapItem.d.ts +2 -2
- package/lib/utils/mapItem.js +3 -3
- package/lib/utils/mapItem.js.map +1 -1
- package/lib/utils/qItem.d.ts +2 -0
- package/lib/utils/qItem.js +22 -0
- package/lib/utils/qItem.js.map +1 -1
- package/lib/utils/qrItem.d.ts +1 -1
- package/lib/utils/qrItem.js +92 -93
- package/lib/utils/qrItem.js.map +1 -1
- package/lib/utils/questionnaireStoreUtils/createQuestionaireModel.js +4 -0
- package/lib/utils/questionnaireStoreUtils/createQuestionaireModel.js.map +1 -1
- package/lib/utils/repopulate.d.ts +9 -0
- package/lib/utils/repopulate.js +133 -0
- package/lib/utils/repopulate.js.map +1 -0
- package/lib/utils/repopulateGenerateItems.d.ts +9 -0
- package/lib/utils/repopulateGenerateItems.js +133 -0
- package/lib/utils/repopulateGenerateItems.js.map +1 -0
- package/lib/utils/repopulateIntoResponse.d.ts +3 -0
- package/lib/utils/repopulateIntoResponse.js +92 -0
- package/lib/utils/repopulateIntoResponse.js.map +1 -0
- package/lib/utils/repopulateItems.d.ts +9 -0
- package/lib/utils/repopulateItems.js +131 -0
- package/lib/utils/repopulateItems.js.map +1 -0
- package/lib/utils/repopulateRepeatGroup.d.ts +4 -0
- package/lib/utils/repopulateRepeatGroup.js +54 -0
- package/lib/utils/repopulateRepeatGroup.js.map +1 -0
- package/package.json +3 -1
- package/src/components/FormComponents/ChoiceItems/ChoiceAutocompleteItem.tsx +1 -1
- package/src/components/FormComponents/DecimalItem/DecimalItem.tsx +3 -2
- package/src/components/FormComponents/GridGroup/GridGroup.tsx +2 -2
- package/src/components/FormComponents/GridGroup/GridRow.tsx +2 -2
- package/src/components/FormComponents/GroupItem/GroupItem.tsx +3 -3
- package/src/components/FormComponents/IntegerItem/IntegerItem.tsx +3 -2
- package/src/components/FormComponents/OpenChoiceItems/OpenChoiceAutocompleteItem.tsx +1 -1
- package/src/components/FormComponents/RepeatGroup/RepeatGroup.tsx +4 -3
- package/src/{theme/overrides/Backdrop.ts → components/FormComponents/RepeatGroup/index.ts} +1 -17
- package/src/components/FormComponents/RepeatItem/RepeatItem.tsx +1 -1
- package/src/components/FormComponents/SingleItem/index.ts +18 -0
- package/src/components/FormComponents/StringItem/StringItem.tsx +3 -2
- package/src/components/FormComponents/Tables/QItemGroupTable.tsx +15 -6
- package/src/components/FormComponents/Tables/QItemGroupTableRow.tsx +2 -2
- package/src/components/FormComponents/Tables/index.ts +18 -0
- package/src/components/FormComponents/TextItem/TextItem.tsx +3 -2
- package/src/components/FormComponents/index.ts +20 -0
- package/src/components/Renderer/BaseRenderer.tsx +39 -18
- package/src/components/Renderer/FormBodyCollapsible.tsx +2 -2
- package/src/components/Renderer/FormBodyTabbed.tsx +2 -2
- package/src/components/Renderer/FormTopLevelItem.tsx +33 -4
- package/src/components/index.ts +1 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useInitialiseRepeatAnswers.ts +28 -17
- package/src/hooks/useInitialiseRepeatGroups.ts +28 -17
- package/src/hooks/useNumberInput.ts +37 -0
- package/src/hooks/useStringInput.ts +37 -0
- package/src/index.ts +43 -0
- package/src/interfaces/questionnaireStore.interface.ts +1 -0
- package/src/stores/useQuestionnaireResponseStore.ts +14 -10
- package/src/stores/useQuestionnaireStore.ts +10 -3
- package/src/theme/overrides/Overrides.ts +0 -2
- package/src/utils/calculatedExpression.ts +2 -2
- package/src/utils/formChanges.ts +141 -0
- package/src/utils/index.ts +18 -0
- package/src/utils/mapItem.ts +6 -4
- package/src/utils/qItem.ts +29 -0
- package/src/utils/qrItem.ts +104 -94
- package/src/utils/questionnaireStoreUtils/createQuestionaireModel.ts +4 -0
- package/src/utils/repopulateIntoResponse.ts +153 -0
- package/src/utils/repopulateItems.ts +207 -0
- package/src/utils/repopulateRepeatGroup.ts +68 -0
- package/lib/theme/overrides/Typography.d.ts +0 -13
- package/lib/theme/overrides/Typography.js.map +0 -1
- package/lib/utils/isHidden.d.ts +0 -3
- package/lib/utils/isHidden.js.map +0 -1
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
import React from 'react';
|
|
19
19
|
import type { QuestionnaireItem, QuestionnaireResponseItem } from 'fhir/r4';
|
|
20
|
-
import { createQrGroup,
|
|
20
|
+
import { createQrGroup, updateQrItemsInGroup } from '../../../utils/qrItem';
|
|
21
21
|
import SingleItem from '../SingleItem/SingleItem';
|
|
22
22
|
import { getQrItemsIndex } from '../../../utils/mapItem';
|
|
23
23
|
import { StandardTableCell } from './Table.styles';
|
|
@@ -42,7 +42,7 @@ function QItemGroupTableRow(props: Props) {
|
|
|
42
42
|
|
|
43
43
|
function handleQrRowItemChange(newQrRowItem: QuestionnaireResponseItem) {
|
|
44
44
|
const qrRow: QuestionnaireResponseItem = { ...row };
|
|
45
|
-
|
|
45
|
+
updateQrItemsInGroup(newQrRowItem, null, qrRow, qItemsIndexMap);
|
|
46
46
|
onQrItemChange(qrRow);
|
|
47
47
|
}
|
|
48
48
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 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
|
+
export { default as GroupTable } from './QItemGroupTable';
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import React, { useCallback
|
|
18
|
+
import React, { useCallback } from 'react';
|
|
19
19
|
import type {
|
|
20
20
|
PropsWithIsRepeatedAttribute,
|
|
21
21
|
PropsWithQrItemChangeHandler
|
|
@@ -30,6 +30,7 @@ import { FullWidthFormComponentBox } from '../../Box.styles';
|
|
|
30
30
|
import TextField from './TextField';
|
|
31
31
|
import useStringCalculatedExpression from '../../../hooks/useStringCalculatedExpression';
|
|
32
32
|
import ItemFieldGrid from '../ItemParts/ItemFieldGrid';
|
|
33
|
+
import useStringInput from '../../../hooks/useStringInput';
|
|
33
34
|
|
|
34
35
|
interface TextItemProps
|
|
35
36
|
extends PropsWithQrItemChangeHandler<QuestionnaireResponseItem>,
|
|
@@ -57,7 +58,7 @@ function TextItem(props: TextItemProps) {
|
|
|
57
58
|
if (qrItem?.answer && qrItem?.answer[0].valueString) {
|
|
58
59
|
valueText = qrItem.answer[0].valueString;
|
|
59
60
|
}
|
|
60
|
-
const [input, setInput] =
|
|
61
|
+
const [input, setInput] = useStringInput(valueText);
|
|
61
62
|
|
|
62
63
|
// Perform validation checks
|
|
63
64
|
const feedback = useValidationError(input, regexValidation, maxLength);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 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
|
+
export * from './SingleItem';
|
|
19
|
+
export * from './RepeatGroup';
|
|
20
|
+
export * from './Tables';
|
|
@@ -15,13 +15,17 @@
|
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import React from 'react';
|
|
18
|
+
import React, { useMemo } from 'react';
|
|
19
19
|
import Container from '@mui/material/Container';
|
|
20
20
|
import Fade from '@mui/material/Fade';
|
|
21
21
|
import FormTopLevelItem from './FormTopLevelItem';
|
|
22
22
|
import type { QuestionnaireResponse, QuestionnaireResponseItem } from 'fhir/r4';
|
|
23
23
|
import useQuestionnaireStore from '../../stores/useQuestionnaireStore';
|
|
24
24
|
import useQuestionnaireResponseStore from '../../stores/useQuestionnaireResponseStore';
|
|
25
|
+
import cloneDeep from 'lodash.clonedeep';
|
|
26
|
+
import { getQrItemsIndex, mapQItemsIndex } from '../../utils/mapItem';
|
|
27
|
+
import { updateQrItemsInGroup } from '../../utils/qrItem';
|
|
28
|
+
import type { QrRepeatGroup } from '../../interfaces/repeatGroup.interface';
|
|
25
29
|
|
|
26
30
|
function BaseRenderer() {
|
|
27
31
|
const sourceQuestionnaire = useQuestionnaireStore((state) => state.sourceQuestionnaire);
|
|
@@ -29,48 +33,65 @@ function BaseRenderer() {
|
|
|
29
33
|
const updatableResponse = useQuestionnaireResponseStore((state) => state.updatableResponse);
|
|
30
34
|
const updateResponse = useQuestionnaireResponseStore((state) => state.updateResponse);
|
|
31
35
|
|
|
32
|
-
|
|
33
|
-
|
|
36
|
+
const qItemsIndexMap = useMemo(() => mapQItemsIndex(sourceQuestionnaire), [sourceQuestionnaire]);
|
|
37
|
+
|
|
38
|
+
function handleTopLevelQRItemSingleChange(
|
|
39
|
+
newTopLevelQRItem: QuestionnaireResponseItem,
|
|
40
|
+
index: number
|
|
41
|
+
) {
|
|
42
|
+
const updatedResponse: QuestionnaireResponse = cloneDeep(updatableResponse);
|
|
43
|
+
if (!updatedResponse.item || updatedResponse.item.length === 0) {
|
|
34
44
|
return;
|
|
35
45
|
}
|
|
36
46
|
|
|
37
|
-
const updatedItems = [...
|
|
38
|
-
updatedItems[index] =
|
|
47
|
+
const updatedItems = [...updatedResponse.item]; // Copy the original array of items
|
|
48
|
+
updatedItems[index] = newTopLevelQRItem; // Modify the item at the specified index
|
|
39
49
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
50
|
+
updatedResponse.item = updatedItems;
|
|
51
|
+
|
|
52
|
+
updateExpressions(updatedResponse);
|
|
53
|
+
updateResponse(updatedResponse);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function handleTopLevelQRItemMultipleChange(newTopLevelQRItems: QrRepeatGroup) {
|
|
57
|
+
const updatedResponse: QuestionnaireResponse = cloneDeep(updatableResponse);
|
|
58
|
+
if (!updatedResponse.item || updatedResponse.item.length === 0) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
updateQrItemsInGroup(null, newTopLevelQRItems, updatedResponse, qItemsIndexMap);
|
|
44
63
|
|
|
45
64
|
updateExpressions(updatedResponse);
|
|
46
65
|
updateResponse(updatedResponse);
|
|
47
66
|
}
|
|
48
67
|
|
|
49
68
|
const topLevelQItems = sourceQuestionnaire.item;
|
|
50
|
-
const topLevelQRItems = updatableResponse.item;
|
|
69
|
+
const topLevelQRItems = cloneDeep(updatableResponse.item) ?? [];
|
|
51
70
|
|
|
52
71
|
if (!topLevelQItems) {
|
|
53
72
|
return <>Questionnaire does not have any items</>;
|
|
54
73
|
}
|
|
55
74
|
|
|
75
|
+
// If an item has multiple answers, it is a repeat group
|
|
76
|
+
const topLevelQRItemsByIndex: (QuestionnaireResponseItem | QuestionnaireResponseItem[])[] =
|
|
77
|
+
getQrItemsIndex(topLevelQItems, topLevelQRItems, qItemsIndexMap);
|
|
78
|
+
|
|
56
79
|
return (
|
|
57
80
|
<Fade in={true} timeout={500}>
|
|
58
81
|
<Container maxWidth="xl">
|
|
59
82
|
{topLevelQItems.map((qItem, index) => {
|
|
60
|
-
const
|
|
61
|
-
? topLevelQRItems[index]
|
|
62
|
-
: {
|
|
63
|
-
linkId: qItem.linkId,
|
|
64
|
-
text: qItem.text
|
|
65
|
-
};
|
|
83
|
+
const qrItemOrItems = topLevelQRItemsByIndex[index];
|
|
66
84
|
|
|
67
85
|
return (
|
|
68
86
|
<FormTopLevelItem
|
|
69
87
|
key={qItem.linkId}
|
|
70
88
|
topLevelQItem={qItem}
|
|
71
|
-
|
|
89
|
+
topLevelQRItemOrItems={qrItemOrItems}
|
|
72
90
|
onQrItemChange={(newTopLevelQRItem) =>
|
|
73
|
-
|
|
91
|
+
handleTopLevelQRItemSingleChange(newTopLevelQRItem, index)
|
|
92
|
+
}
|
|
93
|
+
onQrRepeatGroupChange={(newTopLevelQRItems) =>
|
|
94
|
+
handleTopLevelQRItemMultipleChange(newTopLevelQRItems)
|
|
74
95
|
}
|
|
75
96
|
/>
|
|
76
97
|
);
|
|
@@ -19,7 +19,7 @@ import React, { useMemo } from 'react';
|
|
|
19
19
|
import Stack from '@mui/material/Stack';
|
|
20
20
|
import type { QuestionnaireItem, QuestionnaireResponseItem } from 'fhir/r4';
|
|
21
21
|
import { getQrItemsIndex, mapQItemsIndex } from '../../utils/mapItem';
|
|
22
|
-
import {
|
|
22
|
+
import { updateQrItemsInGroup } from '../../utils/qrItem';
|
|
23
23
|
import type { PropsWithQrItemChangeHandler } from '../../interfaces/renderProps.interface';
|
|
24
24
|
import useQuestionnaireStore from '../../stores/useQuestionnaireStore';
|
|
25
25
|
import FormBodySingleCollapsibleWrapper from './FormBodySingleCollapsibleWrapper';
|
|
@@ -46,7 +46,7 @@ function FormBodyCollapsibleWrapper(props: FormBodyCollapsibleProps) {
|
|
|
46
46
|
const qrItems = topLevelQRItem.item;
|
|
47
47
|
|
|
48
48
|
function handleQrGroupChange(qrItem: QuestionnaireResponseItem) {
|
|
49
|
-
|
|
49
|
+
updateQrItemsInGroup(qrItem, null, topLevelQRItem, indexMap);
|
|
50
50
|
onQrItemChange(topLevelQRItem);
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -22,7 +22,7 @@ import TabContext from '@mui/lab/TabContext';
|
|
|
22
22
|
import TabPanel from '@mui/lab/TabPanel';
|
|
23
23
|
import { getQrItemsIndex, mapQItemsIndex } from '../../utils/mapItem';
|
|
24
24
|
import GroupItem from '../FormComponents/GroupItem/GroupItem';
|
|
25
|
-
import {
|
|
25
|
+
import { updateQrItemsInGroup } from '../../utils/qrItem';
|
|
26
26
|
import FormBodyTabListWrapper from '../Tabs/FormBodyTabListWrapper';
|
|
27
27
|
import type { PropsWithQrItemChangeHandler } from '../../interfaces/renderProps.interface';
|
|
28
28
|
import useQuestionnaireStore from '../../stores/useQuestionnaireStore';
|
|
@@ -47,7 +47,7 @@ function FormBodyTabbed(props: FormBodyTabbedProps) {
|
|
|
47
47
|
const qrItems = topLevelQRItem.item;
|
|
48
48
|
|
|
49
49
|
function handleQrGroupChange(qrItem: QuestionnaireResponseItem) {
|
|
50
|
-
|
|
50
|
+
updateQrItemsInGroup(qrItem, null, topLevelQRItem, indexMap);
|
|
51
51
|
onQrItemChange(topLevelQRItem);
|
|
52
52
|
}
|
|
53
53
|
|
|
@@ -21,17 +21,24 @@ import FormBodyTabbed from './FormBodyTabbed';
|
|
|
21
21
|
import { containsTabs, isTabContainer } from '../../utils/tabs';
|
|
22
22
|
import GroupItem from '../FormComponents/GroupItem/GroupItem';
|
|
23
23
|
import SingleItem from '../FormComponents/SingleItem/SingleItem';
|
|
24
|
-
import type {
|
|
24
|
+
import type {
|
|
25
|
+
PropsWithQrItemChangeHandler,
|
|
26
|
+
PropsWithQrRepeatGroupChangeHandler
|
|
27
|
+
} from '../../interfaces/renderProps.interface';
|
|
25
28
|
import FormBodyCollapsible from './FormBodyCollapsible';
|
|
26
29
|
import useResponsive from '../../hooks/useResponsive';
|
|
30
|
+
import useHidden from '../../hooks/useHidden';
|
|
31
|
+
import GroupItemSwitcher from '../FormComponents/GroupItem/GroupItemSwitcher';
|
|
27
32
|
|
|
28
|
-
interface FormTopLevelItemProps
|
|
33
|
+
interface FormTopLevelItemProps
|
|
34
|
+
extends PropsWithQrItemChangeHandler<QuestionnaireResponseItem>,
|
|
35
|
+
PropsWithQrRepeatGroupChangeHandler {
|
|
29
36
|
topLevelQItem: QuestionnaireItem;
|
|
30
|
-
|
|
37
|
+
topLevelQRItemOrItems: QuestionnaireResponseItem | QuestionnaireResponseItem[];
|
|
31
38
|
}
|
|
32
39
|
|
|
33
40
|
function FormTopLevelItem(props: FormTopLevelItemProps) {
|
|
34
|
-
const { topLevelQItem,
|
|
41
|
+
const { topLevelQItem, topLevelQRItemOrItems, onQrItemChange, onQrRepeatGroupChange } = props;
|
|
35
42
|
|
|
36
43
|
const itemIsTabContainer = isTabContainer(topLevelQItem);
|
|
37
44
|
const itemContainsTabs = containsTabs(topLevelQItem);
|
|
@@ -40,6 +47,28 @@ function FormTopLevelItem(props: FormTopLevelItemProps) {
|
|
|
40
47
|
|
|
41
48
|
const itemIsGroup = topLevelQItem.type === 'group';
|
|
42
49
|
|
|
50
|
+
const itemIsHidden = useHidden(topLevelQItem);
|
|
51
|
+
if (itemIsHidden) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// If item has multiple answers, use a group item switcher to determine how to render it.
|
|
56
|
+
const hasMultipleAnswers = Array.isArray(topLevelQRItemOrItems);
|
|
57
|
+
if (hasMultipleAnswers) {
|
|
58
|
+
return (
|
|
59
|
+
<GroupItemSwitcher
|
|
60
|
+
qItem={topLevelQItem}
|
|
61
|
+
qrItemOrItems={topLevelQRItemOrItems}
|
|
62
|
+
groupCardElevation={1}
|
|
63
|
+
onQrItemChange={onQrItemChange}
|
|
64
|
+
onQrRepeatGroupChange={onQrRepeatGroupChange}
|
|
65
|
+
/>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// At this point, item only has one answer
|
|
70
|
+
const topLevelQRItem = topLevelQRItemOrItems;
|
|
71
|
+
|
|
43
72
|
// If form is tabbed, it is rendered as a tabbed form
|
|
44
73
|
if (itemContainsTabs || itemIsTabContainer) {
|
|
45
74
|
if (isDesktop) {
|
package/src/components/index.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as useHidden } from './useHidden';
|
|
@@ -15,28 +15,39 @@
|
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import type { QuestionnaireResponseItem } from 'fhir/r4';
|
|
18
|
+
import type { QuestionnaireItem, QuestionnaireResponseItem } from 'fhir/r4';
|
|
19
19
|
import type { RepeatAnswer } from '../interfaces/repeatItem.interface';
|
|
20
20
|
import { nanoid } from 'nanoid';
|
|
21
|
+
import { useMemo } from 'react';
|
|
21
22
|
|
|
22
|
-
function useInitialiseRepeatAnswers(
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
function useInitialiseRepeatAnswers(
|
|
24
|
+
qItem: QuestionnaireItem,
|
|
25
|
+
qrItem: QuestionnaireResponseItem
|
|
26
|
+
): RepeatAnswer[] {
|
|
27
|
+
return useMemo(
|
|
28
|
+
() => {
|
|
29
|
+
let initialRepeatAnswers: RepeatAnswer[] = [
|
|
30
|
+
{
|
|
31
|
+
nanoId: nanoid(),
|
|
32
|
+
answer: null
|
|
33
|
+
}
|
|
34
|
+
];
|
|
29
35
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
if (qrItem?.answer) {
|
|
37
|
+
initialRepeatAnswers = qrItem.answer.map((answer) => {
|
|
38
|
+
return {
|
|
39
|
+
nanoId: nanoid(),
|
|
40
|
+
answer
|
|
41
|
+
};
|
|
42
|
+
});
|
|
43
|
+
}
|
|
38
44
|
|
|
39
|
-
|
|
45
|
+
return initialRepeatAnswers;
|
|
46
|
+
},
|
|
47
|
+
// init initialRepeatAnswers on first render only, leave dependency array empty
|
|
48
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
49
|
+
[qItem]
|
|
50
|
+
);
|
|
40
51
|
}
|
|
41
52
|
|
|
42
53
|
export default useInitialiseRepeatAnswers;
|
|
@@ -16,27 +16,38 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import type { QuestionnaireResponseItem } from 'fhir/r4';
|
|
19
|
+
import { QuestionnaireItem } from 'fhir/r4';
|
|
19
20
|
import { nanoid } from 'nanoid';
|
|
20
21
|
import type { RepeatGroupSingle } from '../interfaces/repeatGroup.interface';
|
|
22
|
+
import { useMemo } from 'react';
|
|
21
23
|
|
|
22
|
-
function useInitialiseRepeatGroups(
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
function useInitialiseRepeatGroups(
|
|
25
|
+
qItem: QuestionnaireItem,
|
|
26
|
+
qrItems: QuestionnaireResponseItem[]
|
|
27
|
+
): RepeatGroupSingle[] {
|
|
28
|
+
return useMemo(
|
|
29
|
+
() => {
|
|
30
|
+
let initialRepeatGroupAnswers: RepeatGroupSingle[] = [
|
|
31
|
+
{
|
|
32
|
+
nanoId: nanoid(),
|
|
33
|
+
qrItem: null
|
|
34
|
+
}
|
|
35
|
+
];
|
|
29
36
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
if (qrItems.length > 0) {
|
|
38
|
+
initialRepeatGroupAnswers = qrItems.map((qrItem) => {
|
|
39
|
+
return {
|
|
40
|
+
nanoId: nanoid(),
|
|
41
|
+
qrItem
|
|
42
|
+
};
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
return initialRepeatGroupAnswers;
|
|
46
|
+
},
|
|
47
|
+
// init initialRepeatAnswers on first render only, leave dependency array empty
|
|
48
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
49
|
+
[qItem]
|
|
50
|
+
);
|
|
40
51
|
}
|
|
41
52
|
|
|
42
53
|
export default useInitialiseRepeatGroups;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 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 { Dispatch, SetStateAction, useEffect, useState } from 'react';
|
|
19
|
+
|
|
20
|
+
function useNumberInput(valueFromProps: number): [number, Dispatch<SetStateAction<number>>] {
|
|
21
|
+
const [value, setValue] = useState(valueFromProps);
|
|
22
|
+
|
|
23
|
+
useEffect(
|
|
24
|
+
() => {
|
|
25
|
+
if (value !== valueFromProps) {
|
|
26
|
+
setValue(valueFromProps);
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
// Only trigger this effect if prop value changes
|
|
30
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
31
|
+
[valueFromProps]
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
return [value, setValue];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export default useNumberInput;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 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 { Dispatch, SetStateAction, useEffect, useState } from 'react';
|
|
19
|
+
|
|
20
|
+
function useStringInput(valueFromProps: string): [string, Dispatch<SetStateAction<string>>] {
|
|
21
|
+
const [input, setInput] = useState(valueFromProps);
|
|
22
|
+
|
|
23
|
+
useEffect(
|
|
24
|
+
() => {
|
|
25
|
+
if (input !== valueFromProps) {
|
|
26
|
+
setInput(valueFromProps);
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
// Only trigger this effect if prop value changes
|
|
30
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
31
|
+
[valueFromProps]
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
return [input, setInput];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export default useStringInput;
|
package/src/index.ts
CHANGED
|
@@ -3,9 +3,15 @@ import type { Questionnaire, QuestionnaireResponse } from 'fhir/r4';
|
|
|
3
3
|
import { createQuestionnaireResponse } from './utils/qrItem';
|
|
4
4
|
import useQuestionnaireResponseStore from './stores/useQuestionnaireResponseStore';
|
|
5
5
|
import { removeHiddenAnswers } from './utils/removeHidden';
|
|
6
|
+
import type { ItemToRepopulate } from './utils/repopulateItems';
|
|
7
|
+
import { getItemsToRepopulate } from './utils/repopulateItems';
|
|
8
|
+
import { repopulateItemsIntoResponse } from './utils/repopulateIntoResponse';
|
|
6
9
|
|
|
7
10
|
export * from './components';
|
|
8
11
|
export * from './stores';
|
|
12
|
+
export * from './hooks';
|
|
13
|
+
export * from './utils';
|
|
14
|
+
export type { ItemToRepopulate };
|
|
9
15
|
|
|
10
16
|
/**
|
|
11
17
|
* Build the form with an initial Questionnaire and an optional filled QuestionnaireResponse.
|
|
@@ -72,3 +78,40 @@ export function removeHiddenAnswersFromResponse(
|
|
|
72
78
|
enableWhenExpressions
|
|
73
79
|
});
|
|
74
80
|
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Re-populate stuff
|
|
84
|
+
*
|
|
85
|
+
* @author Sean Fong
|
|
86
|
+
*/
|
|
87
|
+
export function generateItemsToRepopulate(populatedResponse: QuestionnaireResponse) {
|
|
88
|
+
const sourceQuestionnaire = useQuestionnaireStore.getState().sourceQuestionnaire;
|
|
89
|
+
const itemTypes = useQuestionnaireStore.getState().itemTypes;
|
|
90
|
+
const tabs = useQuestionnaireStore.getState().tabs;
|
|
91
|
+
const updatableResponse = useQuestionnaireResponseStore.getState().updatableResponse;
|
|
92
|
+
console.log(updatableResponse);
|
|
93
|
+
|
|
94
|
+
return getItemsToRepopulate(
|
|
95
|
+
sourceQuestionnaire,
|
|
96
|
+
itemTypes,
|
|
97
|
+
tabs,
|
|
98
|
+
populatedResponse,
|
|
99
|
+
updatableResponse
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Re-populate stuff
|
|
105
|
+
*
|
|
106
|
+
* @author Sean Fong
|
|
107
|
+
*/
|
|
108
|
+
export function repopulate(checkedItemsToRepopulate: Record<string, ItemToRepopulate>) {
|
|
109
|
+
const sourceQuestionnaire = useQuestionnaireStore.getState().sourceQuestionnaire;
|
|
110
|
+
const updatableResponse = useQuestionnaireResponseStore.getState().updatableResponse;
|
|
111
|
+
|
|
112
|
+
return repopulateItemsIntoResponse(
|
|
113
|
+
sourceQuestionnaire,
|
|
114
|
+
updatableResponse,
|
|
115
|
+
checkedItemsToRepopulate
|
|
116
|
+
);
|
|
117
|
+
}
|
|
@@ -24,6 +24,7 @@ import type { AnswerExpression } from './answerExpression.interface';
|
|
|
24
24
|
import type { Coding } from 'fhir/r4';
|
|
25
25
|
|
|
26
26
|
export interface QuestionnaireModel {
|
|
27
|
+
itemTypes: Record<string, string>;
|
|
27
28
|
tabs: Tabs;
|
|
28
29
|
variables: Variables;
|
|
29
30
|
launchContexts: Record<string, LaunchContext>;
|
|
@@ -2,11 +2,12 @@ import { create } from 'zustand';
|
|
|
2
2
|
import type { QuestionnaireResponse } from 'fhir/r4';
|
|
3
3
|
import { emptyResponse } from '../utils/emptyResource';
|
|
4
4
|
import cloneDeep from 'lodash.clonedeep';
|
|
5
|
+
import { diff } from 'json-diff';
|
|
5
6
|
|
|
6
7
|
export interface UseQuestionnaireResponseStoreType {
|
|
7
8
|
sourceResponse: QuestionnaireResponse;
|
|
8
9
|
updatableResponse: QuestionnaireResponse;
|
|
9
|
-
|
|
10
|
+
formChangesHistory: object[];
|
|
10
11
|
buildSourceResponse: (response: QuestionnaireResponse) => void;
|
|
11
12
|
setUpdatableResponseAsPopulated: (populatedResponse: QuestionnaireResponse) => void;
|
|
12
13
|
updateResponse: (updatedResponse: QuestionnaireResponse) => void;
|
|
@@ -15,10 +16,10 @@ export interface UseQuestionnaireResponseStoreType {
|
|
|
15
16
|
destroySourceResponse: () => void;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
const useQuestionnaireResponseStore = create<UseQuestionnaireResponseStoreType>()((set) => ({
|
|
19
|
+
const useQuestionnaireResponseStore = create<UseQuestionnaireResponseStoreType>()((set, get) => ({
|
|
19
20
|
sourceResponse: cloneDeep(emptyResponse),
|
|
20
21
|
updatableResponse: cloneDeep(emptyResponse),
|
|
21
|
-
|
|
22
|
+
formChangesHistory: [],
|
|
22
23
|
buildSourceResponse: (questionnaireResponse: QuestionnaireResponse) => {
|
|
23
24
|
set(() => ({
|
|
24
25
|
sourceResponse: questionnaireResponse,
|
|
@@ -26,32 +27,35 @@ const useQuestionnaireResponseStore = create<UseQuestionnaireResponseStoreType>(
|
|
|
26
27
|
}));
|
|
27
28
|
},
|
|
28
29
|
setUpdatableResponseAsPopulated: (populatedResponse: QuestionnaireResponse) => {
|
|
30
|
+
const formChanges = diff(get().updatableResponse, populatedResponse, { full: true });
|
|
29
31
|
set(() => ({
|
|
30
32
|
updatableResponse: populatedResponse,
|
|
31
|
-
|
|
33
|
+
formChangesHistory: [...get().formChangesHistory, formChanges]
|
|
32
34
|
}));
|
|
33
35
|
},
|
|
34
|
-
updateResponse: (updatedResponse: QuestionnaireResponse) =>
|
|
36
|
+
updateResponse: (updatedResponse: QuestionnaireResponse) => {
|
|
37
|
+
const formChanges = diff(get().updatableResponse, updatedResponse, { full: true });
|
|
35
38
|
set(() => ({
|
|
36
39
|
updatableResponse: updatedResponse,
|
|
37
|
-
|
|
38
|
-
}))
|
|
40
|
+
formChangesHistory: [...get().formChangesHistory, formChanges]
|
|
41
|
+
}));
|
|
42
|
+
},
|
|
39
43
|
setUpdatableResponseAsSaved: (savedResponse: QuestionnaireResponse) =>
|
|
40
44
|
set(() => ({
|
|
41
45
|
sourceResponse: savedResponse,
|
|
42
46
|
updatableResponse: savedResponse,
|
|
43
|
-
|
|
47
|
+
formChangesHistory: []
|
|
44
48
|
})),
|
|
45
49
|
setUpdatableResponseAsEmpty: (clearedResponse: QuestionnaireResponse) =>
|
|
46
50
|
set(() => ({
|
|
47
51
|
updatableResponse: clearedResponse,
|
|
48
|
-
|
|
52
|
+
formChangesHistory: []
|
|
49
53
|
})),
|
|
50
54
|
destroySourceResponse: () =>
|
|
51
55
|
set(() => ({
|
|
52
56
|
sourceResponse: cloneDeep(emptyResponse),
|
|
53
57
|
updatableResponse: cloneDeep(emptyResponse),
|
|
54
|
-
|
|
58
|
+
formChangesHistory: []
|
|
55
59
|
}))
|
|
56
60
|
}));
|
|
57
61
|
|