diff --git a/apps/smart-forms-app/package.json b/apps/smart-forms-app/package.json
index 45759b2e2..c1a784f5d 100644
--- a/apps/smart-forms-app/package.json
+++ b/apps/smart-forms-app/package.json
@@ -60,7 +60,7 @@
"@jest/globals": "^29.5.0",
"@sentry/cli": "^2.19.4",
"@tanstack/react-query-devtools": "^4.29.6",
- "@types/fhir": "^0.0.36",
+ "@types/fhir": "^0.0.37",
"@types/jest": "^29.5.2",
"@types/lodash.clonedeep": "^4.5.7",
"@types/lodash.debounce": "^4.0.7",
diff --git a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/QuestionnaireTableView.tsx b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/QuestionnaireTableView.tsx
index 9d92ca720..fd26d7844 100644
--- a/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/QuestionnaireTableView.tsx
+++ b/apps/smart-forms-app/src/features/dashboard/components/DashboardPages/QuestionnairePage/QuestionnaireTableView.tsx
@@ -16,7 +16,6 @@
*/
import QuestionnaireListToolbar from './TableComponents/QuestionnaireListToolbar.tsx';
-import Scrollbar from '../../../../../components/Scrollbar/Scrollbar.tsx';
import { Fade, Table as MuiTable, TableBody, TableContainer, Typography } from '@mui/material';
import DashboardTableHead from '../DashboardTableHead.tsx';
import QuestionnaireTableRow from './TableComponents/QuestionnaireTableRow.tsx';
@@ -71,38 +70,36 @@ function QuestionnaireTableView(props: QuestionnaireTableViewProps) {
onSearch={onSearch}
/>
-
-
-
-
-
- {table.getRowModel().rows.map((row) => {
- const rowData = row.original;
- const isSelected = selectedQuestionnaire?.listItem.id === rowData.id;
+
+
+
+
+ {table.getRowModel().rows.map((row) => {
+ const rowData = row.original;
+ const isSelected = selectedQuestionnaire?.listItem.id === rowData.id;
- return (
- onRowClick(rowData.id)}
- />
- );
- })}
-
+ return (
+ onRowClick(rowData.id)}
+ />
+ );
+ })}
+
- {isEmpty || fetchStatus === 'error' || isInitialLoading ? (
-
- ) : null}
-
-
-
+ {isEmpty || fetchStatus === 'error' || isInitialLoading ? (
+
+ ) : null}
+
+
diff --git a/apps/smart-forms-app/src/features/renderer/components/FormPage/QFormComponents/BooleanItem/BooleanItem.tsx b/apps/smart-forms-app/src/features/renderer/components/FormPage/QFormComponents/BooleanItem/BooleanItem.tsx
index aad9542f2..c772a0bb6 100644
--- a/apps/smart-forms-app/src/features/renderer/components/FormPage/QFormComponents/BooleanItem/BooleanItem.tsx
+++ b/apps/smart-forms-app/src/features/renderer/components/FormPage/QFormComponents/BooleanItem/BooleanItem.tsx
@@ -26,7 +26,6 @@ import { createEmptyQrItem } from '../../../../utils/qrItem.ts';
import { FullWidthFormComponentBox } from '../../../../../../components/Box/Box.styles.tsx';
import FieldGrid from '../FieldGrid.tsx';
import BooleanField from './BooleanField.tsx';
-import useInitialiseBooleanFalse from '../../../../hooks/useInitialiseBooleanFalse.ts';
import { Box } from '@mui/material';
interface BooleanItemProps
@@ -49,8 +48,6 @@ function BooleanItem(props: BooleanItemProps) {
checked = qrItem.answer[0].valueBoolean;
}
- useInitialiseBooleanFalse(qItem, qrItem, onQrItemChange);
-
// Event handlers
function handleCheckedChange(newChecked: boolean) {
onQrItemChange({
diff --git a/apps/smart-forms-app/src/features/renderer/components/FormPage/QFormComponents/DisplayItem/DisplayItem.tsx b/apps/smart-forms-app/src/features/renderer/components/FormPage/QFormComponents/DisplayItem/DisplayItem.tsx
index 91fb7cb50..6e755b155 100644
--- a/apps/smart-forms-app/src/features/renderer/components/FormPage/QFormComponents/DisplayItem/DisplayItem.tsx
+++ b/apps/smart-forms-app/src/features/renderer/components/FormPage/QFormComponents/DisplayItem/DisplayItem.tsx
@@ -19,6 +19,7 @@ import { memo } from 'react';
import type { QuestionnaireItem } from 'fhir/r4';
import { FullWidthFormComponentBox } from '../../../../../../components/Box/Box.styles.tsx';
import LabelText from '../QItemParts/LabelText.tsx';
+import { isSpecificItemControl } from '../../../../utils/itemControl.ts';
interface DisplayItemProps {
qItem: QuestionnaireItem;
@@ -27,6 +28,11 @@ interface DisplayItemProps {
const DisplayItem = memo(function DisplayItem(props: DisplayItemProps) {
const { qItem } = props;
+ const isContextDisplay = isSpecificItemControl(qItem, 'context-display');
+ if (isContextDisplay) {
+ return null;
+ }
+
return (
diff --git a/apps/smart-forms-app/src/features/renderer/components/FormPage/QFormComponents/GroupItem/GroupHeading.tsx b/apps/smart-forms-app/src/features/renderer/components/FormPage/QFormComponents/GroupItem/GroupHeading.tsx
index ec2734f20..566c5f9e8 100644
--- a/apps/smart-forms-app/src/features/renderer/components/FormPage/QFormComponents/GroupItem/GroupHeading.tsx
+++ b/apps/smart-forms-app/src/features/renderer/components/FormPage/QFormComponents/GroupItem/GroupHeading.tsx
@@ -19,9 +19,10 @@ import { memo } from 'react';
import { Box, Divider } from '@mui/material';
import { QGroupHeadingTypography } from '../Typography.styles.ts';
import LabelText from '../QItemParts/LabelText.tsx';
-import CompleteTabButton from '../../Tabs/CompleteTabButton.tsx';
import type { PropsWithIsRepeatedAttribute } from '../../../../types/renderProps.interface.ts';
import type { QuestionnaireItem } from 'fhir/r4';
+import { getContextDisplays } from '../../../../utils/tabs.ts';
+import GroupHeadingIcon from './GroupHeadingIcon.tsx';
interface GroupHeadingProps extends PropsWithIsRepeatedAttribute {
qItem: QuestionnaireItem;
@@ -31,6 +32,8 @@ interface GroupHeadingProps extends PropsWithIsRepeatedAttribute {
const GroupHeading = memo(function GroupHeading(props: GroupHeadingProps) {
const { qItem, tabIsMarkedAsComplete, isRepeated } = props;
+ const contextDisplayItems = getContextDisplays(qItem);
+
if (isRepeated) {
return null;
}
@@ -42,12 +45,11 @@ const GroupHeading = memo(function GroupHeading(props: GroupHeadingProps) {
- {tabIsMarkedAsComplete !== undefined ? (
-
- ) : null}
+
+ {contextDisplayItems.map((item) => {
+ return ;
+ })}
+
{qItem.text ? : null}
>
diff --git a/apps/smart-forms-app/src/features/renderer/components/FormPage/QFormComponents/GroupItem/GroupHeadingIcon.tsx b/apps/smart-forms-app/src/features/renderer/components/FormPage/QFormComponents/GroupItem/GroupHeadingIcon.tsx
new file mode 100644
index 000000000..9923f1c30
--- /dev/null
+++ b/apps/smart-forms-app/src/features/renderer/components/FormPage/QFormComponents/GroupItem/GroupHeadingIcon.tsx
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2023 Commonwealth Scientific and Industrial Research
+ * Organisation (CSIRO) ABN 41 687 119 230.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { memo } from 'react';
+import type { QuestionnaireItem } from 'fhir/r4';
+import useHidden from '../../../../hooks/useHidden.ts';
+import LabelText from '../QItemParts/LabelText.tsx';
+
+interface GroupHeadingIconProps {
+ displayItem: QuestionnaireItem;
+}
+
+const GroupHeadingIcon = memo(function GroupHeadingIcon(props: GroupHeadingIconProps) {
+ const { displayItem } = props;
+
+ const itemIsHidden = useHidden(displayItem);
+ if (itemIsHidden) {
+ return null;
+ }
+
+ return ;
+});
+
+export default GroupHeadingIcon;
diff --git a/apps/smart-forms-app/src/features/renderer/components/FormPage/QFormComponents/QItemParts/LabelText.tsx b/apps/smart-forms-app/src/features/renderer/components/FormPage/QFormComponents/QItemParts/LabelText.tsx
index 637466ea1..22e2bb63a 100644
--- a/apps/smart-forms-app/src/features/renderer/components/FormPage/QFormComponents/QItemParts/LabelText.tsx
+++ b/apps/smart-forms-app/src/features/renderer/components/FormPage/QFormComponents/QItemParts/LabelText.tsx
@@ -32,15 +32,16 @@ const LabelText = memo(function LabelText(props: LabelTextProps) {
// parse xHTML if found
const xHtmlString = getXHtmlString(qItem);
+
if (xHtmlString) {
- return {parse(xHtmlString)};
+ return {parse(xHtmlString)};
}
// parse xHTML if found
const markdownString = getMarkdownString(qItem);
if (markdownString) {
return (
-
+
{markdownString}
);
diff --git a/apps/smart-forms-app/src/features/renderer/components/FormPage/QFormComponents/Tables/QItemGroupTable.tsx b/apps/smart-forms-app/src/features/renderer/components/FormPage/QFormComponents/Tables/QItemGroupTable.tsx
index 14c129cf3..0eeb13c94 100644
--- a/apps/smart-forms-app/src/features/renderer/components/FormPage/QFormComponents/Tables/QItemGroupTable.tsx
+++ b/apps/smart-forms-app/src/features/renderer/components/FormPage/QFormComponents/Tables/QItemGroupTable.tsx
@@ -145,11 +145,6 @@ function QItemGroupTable(props: Props) {
answeredQrItem.item = nullableQrItem.item;
}
- console.log('----');
- console.log(qItem);
- console.log(answeredQrItem);
- console.log('----');
-
return (
state.switchTab);
+ const contextDisplayItems = getContextDisplays(qItem);
+
function handleTabClick() {
switchTab(listIndex);
window.scrollTo(0, 0);
}
return (
-
-
- {markedAsComplete ? (
-
-
-
- ) : (
-
-
-
+ <>
+
+
+ {tabLabel}
+
+ {contextDisplayItems.map((item) => {
+ return ;
+ })}
+
-
- )}
-
- {tabLabel}} />
-
+ }
+ />
+
+ >
);
});
diff --git a/apps/smart-forms-app/src/features/renderer/components/FormPage/Tabs/FormBodyTabList.tsx b/apps/smart-forms-app/src/features/renderer/components/FormPage/Tabs/FormBodyTabList.tsx
index 4bed4f101..962062cf5 100644
--- a/apps/smart-forms-app/src/features/renderer/components/FormPage/Tabs/FormBodyTabList.tsx
+++ b/apps/smart-forms-app/src/features/renderer/components/FormPage/Tabs/FormBodyTabList.tsx
@@ -61,15 +61,14 @@ const FormBodyTabList = memo(function FormBodyTabList(props: FormBodyTabListProp
const tabIsSelected = currentTabIndex.toString() === i.toString();
const tabLabel = getShortText(qItem) ?? qItem.text ?? '';
- const tabIsMarkedAsComplete = tabs[qItem.linkId].isComplete ?? false;
return (
);
diff --git a/apps/smart-forms-app/src/features/renderer/hooks/useInitialiseBooleanFalse.ts b/apps/smart-forms-app/src/features/renderer/hooks/useInitialiseBooleanFalse.ts
deleted file mode 100644
index e5c58acef..000000000
--- a/apps/smart-forms-app/src/features/renderer/hooks/useInitialiseBooleanFalse.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2023 Commonwealth Scientific and Industrial Research
- * Organisation (CSIRO) ABN 41 687 119 230.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import type { QuestionnaireItem, QuestionnaireResponseItem } from 'fhir/r4';
-import useQuestionnaireStore from '../../../stores/useQuestionnaireStore.ts';
-import { useEffect } from 'react';
-import { createEmptyQrItem } from '../utils/qrItem.ts';
-
-function useInitialiseBooleanFalse(
- questionnaireItem: QuestionnaireItem,
- questionnaireResponseItem: QuestionnaireResponseItem,
- onQrItemChange: (qrItem: QuestionnaireResponseItem) => unknown
-) {
- const initialValueBoolean = questionnaireResponseItem?.answer?.[0].valueBoolean;
-
- // Trigger enableWhen on init - special case
- const enableWhenLinkedQuestions = useQuestionnaireStore(
- (state) => state.enableWhenLinkedQuestions
- );
- useEffect(
- () => {
- // if boolean item is an enableWhen linked question and it does not have an answer yet
- // set default answer to false - to trigger enableWhen == false
- if (
- questionnaireItem.linkId in enableWhenLinkedQuestions &&
- typeof initialValueBoolean === 'undefined'
- ) {
- onQrItemChange({
- ...createEmptyQrItem(questionnaireItem),
- answer: [{ valueBoolean: false }]
- });
- }
- },
- // Only run effect on init
- // eslint-disable-next-line react-hooks/exhaustive-deps
- []
- );
-}
-
-export default useInitialiseBooleanFalse;
diff --git a/apps/smart-forms-app/src/features/renderer/utils/itemControl.ts b/apps/smart-forms-app/src/features/renderer/utils/itemControl.ts
index 0e193f6d2..9259a20da 100644
--- a/apps/smart-forms-app/src/features/renderer/utils/itemControl.ts
+++ b/apps/smart-forms-app/src/features/renderer/utils/itemControl.ts
@@ -168,7 +168,26 @@ export function getDecimalPrecision(qItem: QuestionnaireItem): number | null {
* @author Sean Fong
*/
export function getXHtmlString(qItem: QuestionnaireItem): string | null {
- const itemControl = qItem.extension?.find(
+ let xHtmlString = null;
+ if (qItem.extension) {
+ xHtmlString = getXHtmlStringFromExtension(qItem.extension);
+ if (xHtmlString) {
+ return xHtmlString;
+ }
+ }
+
+ if (qItem._text?.extension) {
+ xHtmlString = getXHtmlStringFromExtension(qItem._text?.extension);
+ if (xHtmlString) {
+ return xHtmlString;
+ }
+ }
+
+ return null;
+}
+
+export function getXHtmlStringFromExtension(extensions: Extension[]): string | null {
+ const itemControl = extensions?.find(
(extension: Extension) =>
extension.url === 'http://hl7.org/fhir/StructureDefinition/rendering-xhtml'
);
@@ -178,6 +197,7 @@ export function getXHtmlString(qItem: QuestionnaireItem): string | null {
return itemControl.valueString;
}
}
+
return null;
}
diff --git a/apps/smart-forms-app/src/features/renderer/utils/tabs.ts b/apps/smart-forms-app/src/features/renderer/utils/tabs.ts
index 09673c7bb..5109b8bd7 100644
--- a/apps/smart-forms-app/src/features/renderer/utils/tabs.ts
+++ b/apps/smart-forms-app/src/features/renderer/utils/tabs.ts
@@ -202,3 +202,14 @@ export function findNumOfVisibleTabs(
});
return tabsWithVisibility.filter((tab) => tab.isVisible).length;
}
+
+export function getContextDisplays(item: QuestionnaireItem): QuestionnaireItem[] {
+ if (!item.item || item.item.length === 0) {
+ return [];
+ }
+
+ return item.item.filter(
+ (childItem) =>
+ isSpecificItemControl(childItem, 'context-display') && childItem.type === 'display'
+ );
+}
diff --git a/apps/smart-forms-app/src/utils/enableWhen.ts b/apps/smart-forms-app/src/utils/enableWhen.ts
index 60a809d88..4b9b21c08 100644
--- a/apps/smart-forms-app/src/utils/enableWhen.ts
+++ b/apps/smart-forms-app/src/utils/enableWhen.ts
@@ -153,7 +153,7 @@ export function readInitialAnswers(
const initialValuesMap: Record = {};
questionnaireResponse.item.forEach((item) => {
- readQuestionnaireResponseItem(item, initialValuesMap, linkedQuestionsMap);
+ readQuestionnaireResponseItemRecursive(item, initialValuesMap, linkedQuestionsMap);
});
return initialValuesMap;
}
@@ -163,7 +163,7 @@ export function readInitialAnswers(
*
* @author Sean Fong
*/
-function readQuestionnaireResponseItem(
+function readQuestionnaireResponseItemRecursive(
item: QuestionnaireResponseItem,
initialValues: Record,
linkedQuestionsMap: Record
@@ -172,7 +172,7 @@ function readQuestionnaireResponseItem(
if (items && items.length > 0) {
// iterate through items of item recursively
items.forEach((item) => {
- readQuestionnaireResponseItem(item, initialValues, linkedQuestionsMap);
+ readQuestionnaireResponseItemRecursive(item, initialValues, linkedQuestionsMap);
});
return;
}
@@ -264,6 +264,7 @@ export function assignPopulatedAnswersToEnableWhen(
): { initialisedItems: EnableWhenItems; linkedQuestions: Record } {
const linkedQuestions = createEnableWhenLinkedQuestions(items);
const initialAnswers = readInitialAnswers(questionnaireResponse, linkedQuestions);
+ items = initialiseBooleanFalses(items);
const initialisedItems =
Object.keys(initialAnswers).length > 0
@@ -272,3 +273,31 @@ export function assignPopulatedAnswersToEnableWhen(
return { initialisedItems, linkedQuestions };
}
+
+function initialiseBooleanFalses(items: EnableWhenItems): EnableWhenItems {
+ for (const linkId in items) {
+ const checkedIsEnabledItems: boolean[] = [];
+ const enableWhenItemProperties = items[linkId];
+
+ for (const linkedItem of enableWhenItemProperties.linked) {
+ if (linkedItem.enableWhen.answerBoolean === true && linkedItem.enableWhen.operator === '!=') {
+ checkedIsEnabledItems.push(true);
+ continue;
+ }
+
+ if (linkedItem.enableWhen.answerBoolean === false && linkedItem.enableWhen.operator === '=') {
+ checkedIsEnabledItems.push(true);
+ continue;
+ }
+
+ checkedIsEnabledItems.push(false);
+ }
+
+ enableWhenItemProperties.isEnabled =
+ enableWhenItemProperties.enableBehavior === 'any'
+ ? checkedIsEnabledItems.some((isEnabled) => isEnabled)
+ : checkedIsEnabledItems.every((isEnabled) => isEnabled);
+ }
+
+ return items;
+}
diff --git a/package-lock.json b/package-lock.json
index 88831ce0e..52fc5af64 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -62,7 +62,7 @@
"@jest/globals": "^29.5.0",
"@sentry/cli": "^2.19.4",
"@tanstack/react-query-devtools": "^4.29.6",
- "@types/fhir": "^0.0.36",
+ "@types/fhir": "^0.0.37",
"@types/jest": "^29.5.2",
"@types/lodash.clonedeep": "^4.5.7",
"@types/lodash.debounce": "^4.0.7",
@@ -157,6 +157,12 @@
}
}
},
+ "apps/smart-forms-app/node_modules/@types/fhir": {
+ "version": "0.0.37",
+ "resolved": "https://registry.npmjs.org/@types/fhir/-/fhir-0.0.37.tgz",
+ "integrity": "sha512-fR1y6tPfDmxYDWN4JkJhuI5F5QpbaFVSoNo3pu9A6nzuoojANqg0UBnNZTVegTz/MilV3PSjyvFe6/vO55geKA==",
+ "dev": true
+ },
"apps/smart-forms-app/node_modules/date-fns": {
"version": "2.30.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz",