@aehrc/smart-forms-renderer 0.36.1 → 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/CHANGELOG.md +40 -0
- package/lib/components/FormComponents/Button.styles.d.ts +4 -0
- package/lib/components/FormComponents/Button.styles.js +10 -0
- package/lib/components/FormComponents/Button.styles.js.map +1 -0
- package/lib/components/FormComponents/GroupItem/GroupHeading.d.ts +1 -0
- package/lib/components/FormComponents/GroupItem/GroupHeading.js +3 -2
- package/lib/components/FormComponents/GroupItem/GroupHeading.js.map +1 -1
- package/lib/components/FormComponents/GroupItem/GroupItem.d.ts +4 -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/GroupItemView.d.ts +4 -0
- package/lib/components/FormComponents/GroupItem/GroupItemView.js +8 -5
- package/lib/components/FormComponents/GroupItem/GroupItemView.js.map +1 -1
- package/lib/components/FormComponents/GroupItem/NextPageButton.d.ts +7 -0
- package/lib/{hooks/useQueryClient.js → components/FormComponents/GroupItem/NextPageButton.js} +9 -12
- package/lib/components/FormComponents/GroupItem/NextPageButton.js.map +1 -0
- package/lib/components/FormComponents/GroupItem/PageButtonWrapper.d.ts +8 -0
- package/lib/components/FormComponents/GroupItem/PageButtonWrapper.js +46 -0
- package/lib/components/FormComponents/GroupItem/PageButtonWrapper.js.map +1 -0
- package/lib/components/FormComponents/GroupItem/PreviousPageButton.d.ts +7 -0
- package/lib/components/FormComponents/GroupItem/PreviousPageButton.js +26 -0
- package/lib/components/FormComponents/GroupItem/PreviousPageButton.js.map +1 -0
- 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/{DateTimeItem/DateTimeItem.d.ts → QuantityItem/QuantityItem.d.ts} +3 -3
- 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/components/Renderer/BaseRenderer.js +8 -0
- package/lib/components/Renderer/BaseRenderer.js.map +1 -1
- package/lib/components/Renderer/FormBodyPage.d.ts +9 -0
- package/lib/components/Renderer/FormBodyPage.js +43 -0
- package/lib/components/Renderer/FormBodyPage.js.map +1 -0
- package/lib/components/Renderer/FormTopLevelItem.js +7 -0
- package/lib/components/Renderer/FormTopLevelItem.js.map +1 -1
- package/lib/components/Renderer/FormTopLevelPage.d.ts +9 -0
- package/lib/components/Renderer/FormTopLevelPage.js +29 -0
- package/lib/components/Renderer/FormTopLevelPage.js.map +1 -0
- package/lib/hooks/useDecimalCalculatedExpression.d.ts +2 -2
- package/lib/hooks/useNextAndPreviousVisiblePages.d.ts +7 -0
- package/lib/hooks/useNextAndPreviousVisiblePages.js +47 -0
- package/lib/hooks/useNextAndPreviousVisiblePages.js.map +1 -0
- 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/page.interface.d.ts +16 -0
- package/lib/interfaces/page.interface.js +2 -0
- package/lib/interfaces/page.interface.js.map +1 -0
- package/lib/interfaces/questionnaireStore.interface.d.ts +2 -0
- package/lib/interfaces/valueSet.interface.d.ts +15 -0
- package/lib/stores/questionnaireStore.d.ts +13 -0
- package/lib/stores/questionnaireStore.js +19 -3
- package/lib/stores/questionnaireStore.js.map +1 -1
- package/lib/utils/calculatedExpression.js +4 -1
- package/lib/utils/calculatedExpression.js.map +1 -1
- package/lib/utils/initialise.d.ts +3 -0
- package/lib/utils/initialise.js +6 -1
- package/lib/utils/initialise.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/page.d.ts +43 -0
- package/lib/utils/page.js +101 -0
- package/lib/utils/page.js.map +1 -0
- 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/questionnaireStoreUtils/createQuestionaireModel.js +4 -0
- package/lib/utils/questionnaireStoreUtils/createQuestionaireModel.js.map +1 -1
- package/lib/utils/questionnaireStoreUtils/extractPages.d.ts +3 -0
- package/lib/utils/questionnaireStoreUtils/extractPages.js +18 -0
- package/lib/utils/questionnaireStoreUtils/extractPages.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 +4 -4
- package/src/components/FormComponents/Button.styles.ts +10 -0
- package/src/components/FormComponents/GroupItem/GroupHeading.tsx +5 -3
- package/src/components/FormComponents/GroupItem/GroupItem.tsx +11 -1
- package/src/components/FormComponents/GroupItem/GroupItemView.tsx +12 -0
- package/src/components/FormComponents/GroupItem/NextPageButton.tsx +37 -0
- package/src/components/FormComponents/GroupItem/PageButtonWrapper.tsx +78 -0
- package/src/components/FormComponents/GroupItem/PreviousPageButton.tsx +41 -0
- 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/components/Renderer/BaseRenderer.tsx +21 -0
- package/src/components/Renderer/FormBodyPage.tsx +93 -0
- package/src/components/Renderer/FormTopLevelItem.tsx +17 -0
- package/src/components/Renderer/FormTopLevelPage.tsx +70 -0
- package/src/hooks/useDecimalCalculatedExpression.ts +2 -2
- package/src/hooks/useNextAndPreviousVisiblePages.ts +69 -0
- package/src/hooks/useQuantityCalculatedExpression.ts +177 -0
- package/src/hooks/useRenderingExtensions.ts +5 -2
- package/src/hooks/useStringInput.ts +1 -0
- package/src/interfaces/page.interface.ts +13 -0
- package/src/interfaces/questionnaireStore.interface.ts +2 -0
- package/src/interfaces/valueSet.interface.ts +19 -0
- package/src/stores/questionnaireStore.ts +33 -2
- package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreAllergyIntolerance.json +1 -1
- package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreCondition.json +1 -1
- package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreEncounter.json +137 -58
- package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreImmunization.json +175 -0
- package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreMedicationRequest.json +229 -0
- package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreObservationBP.json +359 -0
- package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreObservationBodyHeight.json +195 -0
- package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreObservationBodyWeight.json +195 -0
- package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreObservationHeartRate.json +195 -0
- package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreObservationSmokingStatus.json +174 -0
- package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCorePatient.json +495 -0
- package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCorePractitioner.json +139 -0
- package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCorePractitionerRole.json +216 -0
- package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreProcedure.json +199 -0
- package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreRespirationRate.json +195 -0
- package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreWaistCircumference.json +195 -0
- package/src/stories/assets/questionnaires/QAuCoreTesting.ts +3342 -1
- package/src/stories/assets/questionnaires/QItemControlGroup.ts +673 -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/stories/sdc/ItemControlGroup.stories.tsx +22 -1
- package/src/stories/testing/AuCoreTester.stories.tsx +140 -1
- package/src/utils/calculatedExpression.ts +5 -1
- package/src/utils/initialise.ts +11 -0
- package/src/utils/itemControl.ts +19 -1
- package/src/utils/page.ts +134 -0
- package/src/utils/quantity.ts +62 -0
- package/src/utils/questionnaireStoreUtils/createQuestionaireModel.ts +5 -0
- package/src/utils/questionnaireStoreUtils/extractPages.ts +24 -0
- package/src/utils/valueSet.ts +32 -1
- package/lib/components/FormComponents/DateTimeItem/DateTimeField.d.ts +0 -12
- package/lib/components/FormComponents/DateTimeItem/DateTimeField.js +0 -34
- package/lib/components/FormComponents/DateTimeItem/DateTimeField.js.map +0 -1
- package/lib/components/FormComponents/DateTimeItem/DateTimeItem.js +0 -60
- package/lib/components/FormComponents/DateTimeItem/DateTimeItem.js.map +0 -1
- package/lib/hooks/useDisplayCalculatedExpression.d.ts +0 -3
- package/lib/hooks/useDisplayCalculatedExpression.js +0 -40
- package/lib/hooks/useDisplayCalculatedExpression.js.map +0 -1
- package/lib/hooks/useInitialiseRenderer.d.ts +0 -4
- package/lib/hooks/useInitialiseRenderer.js +0 -85
- package/lib/hooks/useInitialiseRenderer.js.map +0 -1
- package/lib/hooks/useQueryClient.d.ts +0 -3
- package/lib/hooks/useQueryClient.js.map +0 -1
- package/lib/utils/buildForm.d.ts +0 -8
- package/lib/utils/buildForm.js +0 -26
- package/lib/utils/buildForm.js.map +0 -1
- package/stats.html +0 -4842
- package/stats1.html +0 -4842
- package/stats3.html +0 -4842
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import React, { memo } from 'react';
|
|
2
|
+
import Box from '@mui/material/Box';
|
|
3
|
+
import Typography from '@mui/material/Typography';
|
|
4
|
+
import type { Pages } from '../../../interfaces/page.interface';
|
|
5
|
+
import { useQuestionnaireStore } from '../../../stores';
|
|
6
|
+
import NextPageButton from './NextPageButton';
|
|
7
|
+
import PreviousPageButton from './PreviousPageButton';
|
|
8
|
+
import useNextAndPreviousVisiblePages from '../../../hooks/useNextAndPreviousVisiblePages';
|
|
9
|
+
|
|
10
|
+
interface PageButtonsWrapperProps {
|
|
11
|
+
currentPageIndex?: number;
|
|
12
|
+
pages?: Pages;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const PageButtonsWrapper = memo(function PageButtonsWrapper(props: PageButtonsWrapperProps) {
|
|
16
|
+
const { currentPageIndex, pages } = props;
|
|
17
|
+
|
|
18
|
+
const switchPage = useQuestionnaireStore.use.switchPage();
|
|
19
|
+
|
|
20
|
+
const { previousPageIndex, nextPageIndex, numOfVisiblePages } = useNextAndPreviousVisiblePages(
|
|
21
|
+
currentPageIndex,
|
|
22
|
+
pages
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const pagesNotDefined = currentPageIndex === undefined || pages === undefined;
|
|
26
|
+
|
|
27
|
+
// Event handlers
|
|
28
|
+
function handlePreviousPageButtonClick() {
|
|
29
|
+
if (previousPageIndex === null) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
switchPage(previousPageIndex);
|
|
34
|
+
|
|
35
|
+
// Scroll to top of page
|
|
36
|
+
window.scrollTo(0, 0);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function handleNextPageButtonClick() {
|
|
40
|
+
if (nextPageIndex === null) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
switchPage(nextPageIndex);
|
|
45
|
+
|
|
46
|
+
// Scroll to top of page
|
|
47
|
+
window.scrollTo(0, 0);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (pagesNotDefined) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const previousPageButtonHidden = previousPageIndex === null;
|
|
55
|
+
const nextPageButtonHidden = nextPageIndex === null;
|
|
56
|
+
|
|
57
|
+
// This is more of a fallback check to prevent the user from navigating to an invisble page if buttons are visble for some reason
|
|
58
|
+
const pageButtonsDisabled = numOfVisiblePages <= 1;
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<Box display="flex" mt={3} gap={2} alignItems="center">
|
|
62
|
+
<Box flexGrow={1} />
|
|
63
|
+
<Typography variant="subtitle2" color="text.secondary">
|
|
64
|
+
Page {`${currentPageIndex + 1} / ${numOfVisiblePages}`}
|
|
65
|
+
</Typography>
|
|
66
|
+
<PreviousPageButton
|
|
67
|
+
isDisabled={pageButtonsDisabled || previousPageButtonHidden}
|
|
68
|
+
onPreviousPageClick={handlePreviousPageButtonClick}
|
|
69
|
+
/>
|
|
70
|
+
<NextPageButton
|
|
71
|
+
isDisabled={pageButtonsDisabled || nextPageButtonHidden}
|
|
72
|
+
onNextPageClick={handleNextPageButtonClick}
|
|
73
|
+
/>
|
|
74
|
+
</Box>
|
|
75
|
+
);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
export default PageButtonsWrapper;
|
|
@@ -0,0 +1,41 @@
|
|
|
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 React from 'react';
|
|
19
|
+
import Iconify from '../../Iconify/Iconify';
|
|
20
|
+
import { SecondaryFab } from '../Button.styles';
|
|
21
|
+
|
|
22
|
+
interface PreviousPageButtonProps {
|
|
23
|
+
isDisabled: boolean;
|
|
24
|
+
onPreviousPageClick: () => void;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function PreviousPageButton(props: PreviousPageButtonProps) {
|
|
28
|
+
const { isDisabled, onPreviousPageClick } = props;
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<SecondaryFab
|
|
32
|
+
size="small"
|
|
33
|
+
aria-label="back"
|
|
34
|
+
disabled={isDisabled}
|
|
35
|
+
onClick={onPreviousPageClick}>
|
|
36
|
+
<Iconify icon="material-symbols:chevron-left-rounded" />
|
|
37
|
+
</SecondaryFab>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export default PreviousPageButton;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import Autocomplete from '@mui/material/Autocomplete';
|
|
3
|
+
import type { PropsWithIsTabledAttribute } from '../../../interfaces/renderProps.interface';
|
|
4
|
+
import MuiTextField from '../TextItem/MuiTextField';
|
|
5
|
+
import Box from '@mui/material/Box';
|
|
6
|
+
import Typography from '@mui/material/Typography';
|
|
7
|
+
import type { Quantity } from 'fhir/r4';
|
|
8
|
+
|
|
9
|
+
interface QuantityComparatorFieldProps extends PropsWithIsTabledAttribute {
|
|
10
|
+
linkId: string;
|
|
11
|
+
options: Quantity['comparator'][];
|
|
12
|
+
valueSelect: Quantity['comparator'] | null;
|
|
13
|
+
readOnly: boolean;
|
|
14
|
+
onChange: (newValue: Quantity['comparator'] | null) => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function QuantityComparatorField(props: QuantityComparatorFieldProps) {
|
|
18
|
+
const { linkId, options, valueSelect, readOnly, onChange } = props;
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<Box>
|
|
22
|
+
<Autocomplete
|
|
23
|
+
id={linkId + '-comparator'}
|
|
24
|
+
value={valueSelect ?? null}
|
|
25
|
+
options={options}
|
|
26
|
+
onChange={(_, newValue) => onChange(newValue as Quantity['comparator'])}
|
|
27
|
+
autoHighlight
|
|
28
|
+
sx={{ width: 88 }}
|
|
29
|
+
disabled={readOnly}
|
|
30
|
+
size="small"
|
|
31
|
+
renderInput={(params) => <MuiTextField sx={{ width: 88 }} {...params} />}
|
|
32
|
+
/>
|
|
33
|
+
<Typography variant="caption" color="text.secondary">
|
|
34
|
+
Symbol (optional)
|
|
35
|
+
</Typography>
|
|
36
|
+
</Box>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export default QuantityComparatorField;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import InputAdornment from '@mui/material/InputAdornment';
|
|
3
|
+
import FadingCheckIcon from '../ItemParts/FadingCheckIcon';
|
|
4
|
+
import { StandardTextField } from '../Textfield.styles';
|
|
5
|
+
import type { PropsWithIsTabledAttribute } from '../../../interfaces/renderProps.interface';
|
|
6
|
+
|
|
7
|
+
interface QuantityFieldProps extends PropsWithIsTabledAttribute {
|
|
8
|
+
linkId: string;
|
|
9
|
+
input: string;
|
|
10
|
+
feedback: string;
|
|
11
|
+
displayPrompt: string;
|
|
12
|
+
displayUnit: string;
|
|
13
|
+
entryFormat: string;
|
|
14
|
+
readOnly: boolean;
|
|
15
|
+
calcExpUpdated: boolean;
|
|
16
|
+
onInputChange: (value: string) => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function QuantityField(props: QuantityFieldProps) {
|
|
20
|
+
const {
|
|
21
|
+
linkId,
|
|
22
|
+
input,
|
|
23
|
+
feedback,
|
|
24
|
+
displayPrompt,
|
|
25
|
+
displayUnit,
|
|
26
|
+
entryFormat,
|
|
27
|
+
readOnly,
|
|
28
|
+
calcExpUpdated,
|
|
29
|
+
isTabled,
|
|
30
|
+
onInputChange
|
|
31
|
+
} = props;
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<StandardTextField
|
|
35
|
+
id={linkId}
|
|
36
|
+
value={input}
|
|
37
|
+
error={!!feedback}
|
|
38
|
+
onChange={(event) => onInputChange(event.target.value)}
|
|
39
|
+
disabled={readOnly}
|
|
40
|
+
label={displayPrompt}
|
|
41
|
+
placeholder={entryFormat === '' ? '0.0' : entryFormat}
|
|
42
|
+
fullWidth
|
|
43
|
+
isTabled={isTabled}
|
|
44
|
+
size="small"
|
|
45
|
+
inputProps={{ inputMode: 'numeric', pattern: '[0-9]*' }}
|
|
46
|
+
InputProps={{
|
|
47
|
+
endAdornment: (
|
|
48
|
+
<InputAdornment position={'end'}>
|
|
49
|
+
<FadingCheckIcon fadeIn={calcExpUpdated} disabled={readOnly} />
|
|
50
|
+
{displayUnit}
|
|
51
|
+
</InputAdornment>
|
|
52
|
+
)
|
|
53
|
+
}}
|
|
54
|
+
helperText={feedback}
|
|
55
|
+
data-test="q-item-quantity-field"
|
|
56
|
+
/>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export default QuantityField;
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import React, { useCallback, useMemo, useState } from 'react';
|
|
2
|
+
import type {
|
|
3
|
+
PropsWithIsRepeatedAttribute,
|
|
4
|
+
PropsWithIsTabledAttribute,
|
|
5
|
+
PropsWithParentIsReadOnlyAttribute,
|
|
6
|
+
PropsWithQrItemChangeHandler
|
|
7
|
+
} from '../../../interfaces/renderProps.interface';
|
|
8
|
+
import type {
|
|
9
|
+
Quantity,
|
|
10
|
+
QuestionnaireItem,
|
|
11
|
+
QuestionnaireItemAnswerOption,
|
|
12
|
+
QuestionnaireResponseItem
|
|
13
|
+
} from 'fhir/r4';
|
|
14
|
+
import useRenderingExtensions from '../../../hooks/useRenderingExtensions';
|
|
15
|
+
import { FullWidthFormComponentBox } from '../../Box.styles';
|
|
16
|
+
import useValidationFeedback from '../../../hooks/useValidationFeedback';
|
|
17
|
+
import debounce from 'lodash.debounce';
|
|
18
|
+
import { DEBOUNCE_DURATION } from '../../../utils/debounce';
|
|
19
|
+
import { createEmptyQrItem } from '../../../utils/qrItem';
|
|
20
|
+
import ItemFieldGrid from '../ItemParts/ItemFieldGrid';
|
|
21
|
+
import { parseDecimalStringWithPrecision } from '../../../utils/parseInputs';
|
|
22
|
+
import { getDecimalPrecision } from '../../../utils/itemControl';
|
|
23
|
+
import useStringInput from '../../../hooks/useStringInput';
|
|
24
|
+
import useReadOnly from '../../../hooks/useReadOnly';
|
|
25
|
+
import { useQuestionnaireStore } from '../../../stores';
|
|
26
|
+
import Box from '@mui/material/Box';
|
|
27
|
+
import QuantityField from './QuantityField';
|
|
28
|
+
import QuantityUnitField from './QuantityUnitField';
|
|
29
|
+
import {
|
|
30
|
+
createQuantityItemAnswer,
|
|
31
|
+
quantityComparators,
|
|
32
|
+
stringIsComparator
|
|
33
|
+
} from '../../../utils/quantity';
|
|
34
|
+
import QuantityComparatorField from './QuantityComparatorField';
|
|
35
|
+
import useQuantityCalculatedExpression from '../../../hooks/useQuantityCalculatedExpression';
|
|
36
|
+
|
|
37
|
+
interface QuantityItemProps
|
|
38
|
+
extends PropsWithQrItemChangeHandler,
|
|
39
|
+
PropsWithIsRepeatedAttribute,
|
|
40
|
+
PropsWithIsTabledAttribute,
|
|
41
|
+
PropsWithParentIsReadOnlyAttribute {
|
|
42
|
+
qItem: QuestionnaireItem;
|
|
43
|
+
qrItem: QuestionnaireResponseItem | null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function QuantityItem(props: QuantityItemProps) {
|
|
47
|
+
const { qItem, qrItem, isRepeated, isTabled, parentIsReadOnly, onQrItemChange } = props;
|
|
48
|
+
|
|
49
|
+
const onFocusLinkId = useQuestionnaireStore.use.onFocusLinkId();
|
|
50
|
+
|
|
51
|
+
const readOnly = useReadOnly(qItem, parentIsReadOnly);
|
|
52
|
+
const precision = getDecimalPrecision(qItem);
|
|
53
|
+
const { displayUnit, displayPrompt, entryFormat, quantityUnit } = useRenderingExtensions(qItem);
|
|
54
|
+
|
|
55
|
+
// Get units options if present
|
|
56
|
+
const unitOptions = useMemo(
|
|
57
|
+
() =>
|
|
58
|
+
qItem.extension?.filter(
|
|
59
|
+
(f) => f.url === 'http://hl7.org/fhir/StructureDefinition/questionnaire-unitOption'
|
|
60
|
+
) ?? [],
|
|
61
|
+
[qItem]
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
// Init inputs
|
|
65
|
+
let valueQuantity: Quantity = {};
|
|
66
|
+
let initialValueInput = '';
|
|
67
|
+
let initialComparatorInput: Quantity['comparator'] | null = null;
|
|
68
|
+
let initialUnitInput: QuestionnaireItemAnswerOption | null =
|
|
69
|
+
quantityUnit ?? unitOptions?.at(0) ?? null;
|
|
70
|
+
if (qrItem?.answer) {
|
|
71
|
+
if (qrItem?.answer[0].valueQuantity) {
|
|
72
|
+
valueQuantity = qrItem.answer[0].valueQuantity;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
initialValueInput =
|
|
76
|
+
(precision ? valueQuantity.value?.toFixed(precision) : valueQuantity.value?.toString()) || '';
|
|
77
|
+
|
|
78
|
+
if (valueQuantity.comparator && stringIsComparator(valueQuantity.comparator)) {
|
|
79
|
+
initialComparatorInput = valueQuantity.comparator;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (valueQuantity.code && valueQuantity.system) {
|
|
83
|
+
initialUnitInput = {
|
|
84
|
+
valueCoding: {
|
|
85
|
+
code: valueQuantity.code,
|
|
86
|
+
system: valueQuantity.system,
|
|
87
|
+
display: valueQuantity.unit
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// input states
|
|
94
|
+
const [valueInput, setValueInput] = useStringInput(initialValueInput);
|
|
95
|
+
const [comparatorInput, setComparatorInput] = useState<Quantity['comparator'] | null>(
|
|
96
|
+
initialComparatorInput
|
|
97
|
+
);
|
|
98
|
+
const [unitInput, setUnitInput] = useState<QuestionnaireItemAnswerOption | null>(
|
|
99
|
+
initialUnitInput
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
// Perform validation checks
|
|
103
|
+
const feedback = useValidationFeedback(qItem, valueInput);
|
|
104
|
+
|
|
105
|
+
// Process calculated expressions
|
|
106
|
+
const { calcExpUpdated } = useQuantityCalculatedExpression({
|
|
107
|
+
qItem: qItem,
|
|
108
|
+
inputValue: valueInput,
|
|
109
|
+
precision: precision,
|
|
110
|
+
onChangeByCalcExpressionDecimal: (newValueDecimal: number) => {
|
|
111
|
+
setValueInput(
|
|
112
|
+
typeof precision === 'number'
|
|
113
|
+
? newValueDecimal.toFixed(precision)
|
|
114
|
+
: newValueDecimal.toString()
|
|
115
|
+
);
|
|
116
|
+
onQrItemChange({
|
|
117
|
+
...createEmptyQrItem(qItem),
|
|
118
|
+
answer: [
|
|
119
|
+
{
|
|
120
|
+
valueQuantity: {
|
|
121
|
+
value: newValueDecimal,
|
|
122
|
+
unit: unitInput?.valueCoding?.display,
|
|
123
|
+
system: unitInput?.valueCoding?.system,
|
|
124
|
+
code: unitInput?.valueCoding?.code
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
]
|
|
128
|
+
});
|
|
129
|
+
},
|
|
130
|
+
onChangeByCalcExpressionQuantity: (
|
|
131
|
+
newValueDecimal: number,
|
|
132
|
+
newUnitSystem,
|
|
133
|
+
newUnitCode,
|
|
134
|
+
newUnitDisplay
|
|
135
|
+
) => {
|
|
136
|
+
setValueInput(
|
|
137
|
+
typeof precision === 'number'
|
|
138
|
+
? newValueDecimal.toFixed(precision)
|
|
139
|
+
: newValueDecimal.toString()
|
|
140
|
+
);
|
|
141
|
+
onQrItemChange({
|
|
142
|
+
...createEmptyQrItem(qItem),
|
|
143
|
+
answer: [
|
|
144
|
+
{
|
|
145
|
+
valueQuantity: {
|
|
146
|
+
value: newValueDecimal,
|
|
147
|
+
unit: newUnitDisplay,
|
|
148
|
+
system: newUnitSystem,
|
|
149
|
+
code: newUnitCode
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
]
|
|
153
|
+
});
|
|
154
|
+
},
|
|
155
|
+
onChangeByCalcExpressionNull: () => {
|
|
156
|
+
setValueInput('');
|
|
157
|
+
onQrItemChange(createEmptyQrItem(qItem));
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// Event handlers
|
|
162
|
+
function handleComparatorInputChange(newComparatorInput: Quantity['comparator'] | null) {
|
|
163
|
+
setComparatorInput(newComparatorInput);
|
|
164
|
+
|
|
165
|
+
if (!valueInput) return;
|
|
166
|
+
|
|
167
|
+
onQrItemChange({
|
|
168
|
+
...createEmptyQrItem(qItem),
|
|
169
|
+
answer: createQuantityItemAnswer(precision, valueInput, newComparatorInput, unitInput)
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function handleUnitInputChange(newUnitInput: QuestionnaireItemAnswerOption | null) {
|
|
174
|
+
setUnitInput(newUnitInput);
|
|
175
|
+
|
|
176
|
+
if (!valueInput) return;
|
|
177
|
+
|
|
178
|
+
onQrItemChange({
|
|
179
|
+
...createEmptyQrItem(qItem),
|
|
180
|
+
answer: createQuantityItemAnswer(precision, valueInput, comparatorInput, newUnitInput)
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function handleValueInputChange(newInput: string) {
|
|
185
|
+
const parsedNewInput: string = parseDecimalStringWithPrecision(newInput, precision);
|
|
186
|
+
|
|
187
|
+
setValueInput(parsedNewInput);
|
|
188
|
+
updateQrItemWithDebounce(parsedNewInput);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
192
|
+
const updateQrItemWithDebounce = useCallback(
|
|
193
|
+
debounce((parsedNewInput: string) => {
|
|
194
|
+
if (parsedNewInput === '') {
|
|
195
|
+
onQrItemChange(createEmptyQrItem(qItem));
|
|
196
|
+
} else {
|
|
197
|
+
onQrItemChange({
|
|
198
|
+
...createEmptyQrItem(qItem),
|
|
199
|
+
answer: createQuantityItemAnswer(precision, parsedNewInput, comparatorInput, unitInput)
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}, DEBOUNCE_DURATION),
|
|
203
|
+
[onQrItemChange, qItem, displayUnit, precision, comparatorInput, unitInput]
|
|
204
|
+
); // Dependencies are tested, debounce is causing eslint to not recognise dependencies
|
|
205
|
+
|
|
206
|
+
if (isRepeated) {
|
|
207
|
+
return (
|
|
208
|
+
<Box data-test="q-item-quantity-box" display="flex" gap={1}>
|
|
209
|
+
<QuantityComparatorField
|
|
210
|
+
linkId={qItem.linkId}
|
|
211
|
+
options={quantityComparators}
|
|
212
|
+
valueSelect={comparatorInput}
|
|
213
|
+
readOnly={readOnly}
|
|
214
|
+
isTabled={isTabled}
|
|
215
|
+
onChange={handleComparatorInputChange}
|
|
216
|
+
/>
|
|
217
|
+
<QuantityField
|
|
218
|
+
linkId={qItem.linkId}
|
|
219
|
+
input={valueInput}
|
|
220
|
+
feedback={feedback}
|
|
221
|
+
displayPrompt={displayPrompt}
|
|
222
|
+
displayUnit={displayUnit}
|
|
223
|
+
entryFormat={entryFormat}
|
|
224
|
+
readOnly={readOnly}
|
|
225
|
+
calcExpUpdated={calcExpUpdated}
|
|
226
|
+
isTabled={isTabled}
|
|
227
|
+
onInputChange={handleValueInputChange}
|
|
228
|
+
/>
|
|
229
|
+
{unitOptions.length > 0 ? (
|
|
230
|
+
<QuantityUnitField
|
|
231
|
+
linkId={qItem.linkId}
|
|
232
|
+
options={unitOptions}
|
|
233
|
+
valueSelect={unitInput}
|
|
234
|
+
readOnly={readOnly}
|
|
235
|
+
isTabled={isTabled}
|
|
236
|
+
onChange={handleUnitInputChange}
|
|
237
|
+
/>
|
|
238
|
+
) : null}
|
|
239
|
+
</Box>
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return (
|
|
244
|
+
<FullWidthFormComponentBox
|
|
245
|
+
data-test="q-item-quantity-box"
|
|
246
|
+
data-linkid={qItem.linkId}
|
|
247
|
+
onClick={() => onFocusLinkId(qItem.linkId)}>
|
|
248
|
+
<ItemFieldGrid qItem={qItem} readOnly={readOnly}>
|
|
249
|
+
<Box display="flex" gap={1}>
|
|
250
|
+
<QuantityComparatorField
|
|
251
|
+
linkId={qItem.linkId}
|
|
252
|
+
options={quantityComparators}
|
|
253
|
+
valueSelect={comparatorInput}
|
|
254
|
+
readOnly={readOnly}
|
|
255
|
+
isTabled={isTabled}
|
|
256
|
+
onChange={handleComparatorInputChange}
|
|
257
|
+
/>
|
|
258
|
+
<QuantityField
|
|
259
|
+
linkId={qItem.linkId}
|
|
260
|
+
input={valueInput}
|
|
261
|
+
feedback={feedback}
|
|
262
|
+
displayPrompt={displayPrompt}
|
|
263
|
+
displayUnit={displayUnit}
|
|
264
|
+
entryFormat={entryFormat}
|
|
265
|
+
readOnly={readOnly}
|
|
266
|
+
calcExpUpdated={calcExpUpdated}
|
|
267
|
+
isTabled={isTabled}
|
|
268
|
+
onInputChange={handleValueInputChange}
|
|
269
|
+
/>
|
|
270
|
+
{unitOptions.length > 0 ? (
|
|
271
|
+
<QuantityUnitField
|
|
272
|
+
linkId={qItem.linkId}
|
|
273
|
+
options={unitOptions}
|
|
274
|
+
valueSelect={unitInput}
|
|
275
|
+
readOnly={readOnly}
|
|
276
|
+
isTabled={isTabled}
|
|
277
|
+
onChange={handleUnitInputChange}
|
|
278
|
+
/>
|
|
279
|
+
) : null}
|
|
280
|
+
</Box>
|
|
281
|
+
</ItemFieldGrid>
|
|
282
|
+
</FullWidthFormComponentBox>
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export default QuantityItem;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { getAnswerOptionLabel } from '../../../utils/openChoice';
|
|
3
|
+
import { StandardTextField, TEXT_FIELD_WIDTH } from '../Textfield.styles';
|
|
4
|
+
import Autocomplete from '@mui/material/Autocomplete';
|
|
5
|
+
import type { QuestionnaireItemAnswerOption } from 'fhir/r4';
|
|
6
|
+
import type { PropsWithIsTabledAttribute } from '../../../interfaces/renderProps.interface';
|
|
7
|
+
|
|
8
|
+
interface QuantityUnitFieldProps extends PropsWithIsTabledAttribute {
|
|
9
|
+
linkId: string;
|
|
10
|
+
options: QuestionnaireItemAnswerOption[];
|
|
11
|
+
valueSelect: QuestionnaireItemAnswerOption | null;
|
|
12
|
+
readOnly: boolean;
|
|
13
|
+
onChange: (newValue: QuestionnaireItemAnswerOption | null) => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function QuantityUnitField(props: QuantityUnitFieldProps) {
|
|
17
|
+
const { linkId, options, valueSelect, readOnly, isTabled, onChange } = props;
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<Autocomplete
|
|
21
|
+
id={linkId + '-unit'}
|
|
22
|
+
value={valueSelect ?? null}
|
|
23
|
+
isOptionEqualToValue={(option, value) =>
|
|
24
|
+
option.valueCoding?.code === value?.valueCoding?.code
|
|
25
|
+
}
|
|
26
|
+
options={options}
|
|
27
|
+
getOptionLabel={(option) => getAnswerOptionLabel(option)}
|
|
28
|
+
onChange={(_, newValue) => onChange(newValue as QuestionnaireItemAnswerOption | null)}
|
|
29
|
+
autoHighlight
|
|
30
|
+
sx={{ maxWidth: !isTabled ? TEXT_FIELD_WIDTH : 3000, minWidth: 160, flexGrow: 1 }}
|
|
31
|
+
disabled={readOnly}
|
|
32
|
+
size="small"
|
|
33
|
+
renderInput={(params) => <StandardTextField isTabled={isTabled} {...params} />}
|
|
34
|
+
/>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export default QuantityUnitField;
|
|
@@ -40,6 +40,7 @@ import SliderItem from '../SliderItem/SliderItem';
|
|
|
40
40
|
import IntegerItem from '../IntegerItem/IntegerItem';
|
|
41
41
|
import AttachmentItem from '../AttachmentItem/AttachmentItem';
|
|
42
42
|
import CustomDateTimeItem from '../DateTimeItems/CustomDateTimeItem/CustomDateTimeItem';
|
|
43
|
+
import QuantityItem from '../QuantityItem/QuantityItem';
|
|
43
44
|
|
|
44
45
|
interface SingleItemSwitcherProps
|
|
45
46
|
extends PropsWithQrItemChangeHandler,
|
|
@@ -219,7 +220,7 @@ function SingleItemSwitcher(props: SingleItemSwitcherProps) {
|
|
|
219
220
|
case 'quantity':
|
|
220
221
|
// FIXME quantity item uses the same component as decimal item currently
|
|
221
222
|
return (
|
|
222
|
-
<
|
|
223
|
+
<QuantityItem
|
|
223
224
|
qItem={qItem}
|
|
224
225
|
qrItem={qrItem}
|
|
225
226
|
isRepeated={isRepeated}
|
|
@@ -24,7 +24,9 @@ import { useQuestionnaireResponseStore, useQuestionnaireStore } from '../../stor
|
|
|
24
24
|
import cloneDeep from 'lodash.clonedeep';
|
|
25
25
|
import { getQrItemsIndex, mapQItemsIndex } from '../../utils/mapItem';
|
|
26
26
|
import { updateQrItemsInGroup } from '../../utils/qrItem';
|
|
27
|
+
import { everyIsPages } from '../../utils/page';
|
|
27
28
|
import type { QrRepeatGroup } from '../../interfaces/repeatGroup.interface';
|
|
29
|
+
import FormTopLevelPage from './FormTopLevelPage';
|
|
28
30
|
|
|
29
31
|
/**
|
|
30
32
|
* Main component of the form-rendering engine.
|
|
@@ -74,6 +76,25 @@ function BaseRenderer() {
|
|
|
74
76
|
// If an item has multiple answers, it is a repeat group
|
|
75
77
|
const topLevelQRItemsByIndex = getQrItemsIndex(topLevelQItems, topLevelQRItems, qItemsIndexMap);
|
|
76
78
|
|
|
79
|
+
const everyItemIsPage = everyIsPages(topLevelQItems);
|
|
80
|
+
|
|
81
|
+
if (everyItemIsPage) {
|
|
82
|
+
return (
|
|
83
|
+
<Fade in={true} timeout={500}>
|
|
84
|
+
<Container maxWidth="xl">
|
|
85
|
+
<FormTopLevelPage
|
|
86
|
+
topLevelQItems={topLevelQItems}
|
|
87
|
+
topLevelQRItems={topLevelQRItemsByIndex}
|
|
88
|
+
parentIsReadOnly={readOnly}
|
|
89
|
+
onQrItemChange={(newTopLevelQRItem) =>
|
|
90
|
+
handleTopLevelQRItemSingleChange(newTopLevelQRItem)
|
|
91
|
+
}
|
|
92
|
+
/>
|
|
93
|
+
</Container>
|
|
94
|
+
</Fade>
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
77
98
|
return (
|
|
78
99
|
<Fade in={true} timeout={500}>
|
|
79
100
|
<Container maxWidth="xl">
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import Grid from '@mui/material/Grid';
|
|
3
|
+
import type { QuestionnaireItem, QuestionnaireResponseItem } from 'fhir/r4';
|
|
4
|
+
import TabContext from '@mui/lab/TabContext';
|
|
5
|
+
import TabPanel from '@mui/lab/TabPanel';
|
|
6
|
+
import GroupItem from '../FormComponents/GroupItem/GroupItem';
|
|
7
|
+
import type {
|
|
8
|
+
PropsWithParentIsReadOnlyAttribute,
|
|
9
|
+
PropsWithQrItemChangeHandler
|
|
10
|
+
} from '../../interfaces/renderProps.interface';
|
|
11
|
+
import { useQuestionnaireStore } from '../../stores';
|
|
12
|
+
import { getQrItemsIndex, mapQItemsIndex } from '../../utils/mapItem';
|
|
13
|
+
import { createEmptyQrGroup, updateQrItemsInGroup } from '../../utils/qrItem';
|
|
14
|
+
|
|
15
|
+
interface FormBodyPageProps
|
|
16
|
+
extends PropsWithQrItemChangeHandler,
|
|
17
|
+
PropsWithParentIsReadOnlyAttribute {
|
|
18
|
+
topLevelQItem: QuestionnaireItem;
|
|
19
|
+
topLevelQRItem: QuestionnaireResponseItem | null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function FormBodyPage(props: FormBodyPageProps) {
|
|
23
|
+
const { topLevelQItem, topLevelQRItem, parentIsReadOnly, onQrItemChange } = props;
|
|
24
|
+
|
|
25
|
+
const pages = useQuestionnaireStore.use.pages();
|
|
26
|
+
const currentPage = useQuestionnaireStore.use.currentPageIndex();
|
|
27
|
+
|
|
28
|
+
const indexMap: Record<string, number> = useMemo(
|
|
29
|
+
() => mapQItemsIndex(topLevelQItem),
|
|
30
|
+
[topLevelQItem]
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
const nonNullTopLevelQRItem = topLevelQRItem ?? createEmptyQrGroup(topLevelQItem);
|
|
34
|
+
|
|
35
|
+
const qItems = topLevelQItem.item;
|
|
36
|
+
const qrItems = nonNullTopLevelQRItem.item;
|
|
37
|
+
|
|
38
|
+
function handleQrGroupChange(qrItem: QuestionnaireResponseItem) {
|
|
39
|
+
updateQrItemsInGroup(qrItem, null, nonNullTopLevelQRItem, indexMap);
|
|
40
|
+
onQrItemChange(nonNullTopLevelQRItem);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (!qItems || !qrItems) {
|
|
44
|
+
return <>Unable to load form</>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const qrItemsByIndex = getQrItemsIndex(qItems, qrItems, indexMap);
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<Grid container spacing={1.5}>
|
|
51
|
+
<TabContext value={currentPage.toString()}>
|
|
52
|
+
<Grid item xs={12} md={12} lg={12}>
|
|
53
|
+
{qItems.map((qItem, i) => {
|
|
54
|
+
const qrItem = qrItemsByIndex[i];
|
|
55
|
+
|
|
56
|
+
const isNotRepeatGroup = !Array.isArray(qrItem);
|
|
57
|
+
const isPage = !!pages[qItem.linkId];
|
|
58
|
+
|
|
59
|
+
if (!isPage || !isNotRepeatGroup) {
|
|
60
|
+
// Something has gone horribly wrong
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const isRepeated = qItem.repeats ?? false;
|
|
65
|
+
const pageIsMarkedAsComplete = pages[qItem.linkId].isComplete ?? false;
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<TabPanel
|
|
69
|
+
key={qItem.linkId}
|
|
70
|
+
sx={{ p: 0 }}
|
|
71
|
+
value={i.toString()}
|
|
72
|
+
data-test="renderer-page-panel">
|
|
73
|
+
<GroupItem
|
|
74
|
+
qItem={qItem}
|
|
75
|
+
qrItem={qrItem ?? null}
|
|
76
|
+
isRepeated={isRepeated}
|
|
77
|
+
groupCardElevation={1}
|
|
78
|
+
pageIsMarkedAsComplete={pageIsMarkedAsComplete}
|
|
79
|
+
pages={pages}
|
|
80
|
+
currentPageIndex={currentPage}
|
|
81
|
+
parentIsReadOnly={parentIsReadOnly}
|
|
82
|
+
onQrItemChange={handleQrGroupChange}
|
|
83
|
+
/>
|
|
84
|
+
</TabPanel>
|
|
85
|
+
);
|
|
86
|
+
})}
|
|
87
|
+
</Grid>
|
|
88
|
+
</TabContext>
|
|
89
|
+
</Grid>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export default FormBodyPage;
|