diff --git a/apps/smart-forms-app/src/utils/initaliseForm.ts b/apps/smart-forms-app/src/utils/initaliseForm.ts deleted file mode 100644 index d60a16d22..000000000 --- a/apps/smart-forms-app/src/utils/initaliseForm.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { evaluateInitialEnableWhenExpressions } from './enableWhenExpression.ts'; -import { getFirstVisibleTab } from './tabs.ts'; -import type { Expression, QuestionnaireResponse } from 'fhir/r4'; -import type { EnableWhenExpression, EnableWhenItems } from '../types/enableWhen.interface.ts'; -import type { Tabs } from '../features/renderer/types/tab.interface.ts'; -import { assignPopulatedAnswersToEnableWhen } from './enableWhen.ts'; -import type { CalculatedExpression } from '../types/calculatedExpression.interface.ts'; -import { evaluateInitialCalculatedExpressions } from './calculatedExpressions.ts'; - -interface initialFormFromResponseParams { - questionnaireResponse: QuestionnaireResponse; - enableWhenItems: EnableWhenItems; - enableWhenExpressions: Record; - calculatedExpressions: Record; - variablesFhirPath: Record; - tabs: Tabs; -} - -export function initialiseFormFromResponse(params: initialFormFromResponseParams): { - initialEnableWhenItems: EnableWhenItems; - initialEnableWhenLinkedQuestions: Record; - initialEnableWhenExpressions: Record; - initialCalculatedExpressions: Record; - firstVisibleTab: number; -} { - const { - questionnaireResponse, - enableWhenItems, - enableWhenExpressions, - calculatedExpressions, - variablesFhirPath, - tabs - } = params; - - const { initialisedItems, linkedQuestions } = assignPopulatedAnswersToEnableWhen( - enableWhenItems, - questionnaireResponse - ); - - const initialEnableWhenExpressions = evaluateInitialEnableWhenExpressions({ - initialResponse: questionnaireResponse, - enableWhenExpressions: enableWhenExpressions, - variablesFhirPath: variablesFhirPath - }); - - const initialCalculatedExpressions = evaluateInitialCalculatedExpressions({ - initialResponse: questionnaireResponse, - calculatedExpressions: calculatedExpressions, - variablesFhirPath: variablesFhirPath - }); - - const firstVisibleTab = - Object.keys(tabs).length > 0 - ? getFirstVisibleTab(tabs, initialisedItems, initialEnableWhenExpressions) - : 0; - - return { - initialEnableWhenItems: initialisedItems, - initialEnableWhenLinkedQuestions: linkedQuestions, - initialEnableWhenExpressions, - initialCalculatedExpressions, - firstVisibleTab - }; -} diff --git a/apps/smart-forms-app/src/utils/tabs.ts b/apps/smart-forms-app/src/utils/tabs.ts deleted file mode 100644 index 6fa7d5c7f..000000000 --- a/apps/smart-forms-app/src/utils/tabs.ts +++ /dev/null @@ -1,43 +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 { Tabs } from '../features/renderer/types/tab.interface.ts'; -import type { EnableWhenExpression, EnableWhenItems } from '../types/enableWhen.interface.ts'; - -export function getFirstVisibleTab( - tabs: Tabs, - enableWhenItems: EnableWhenItems, - enableWhenExpressions: Record -) { - return Object.entries(tabs) - .sort(([, tabA], [, tabB]) => tabA.tabIndex - tabB.tabIndex) - .findIndex(([tabLinkId, tab]) => { - if (tab.isHidden) { - return false; - } - - if (enableWhenItems[tabLinkId]) { - return enableWhenItems[tabLinkId].isEnabled; - } - - if (enableWhenExpressions[tabLinkId]) { - return enableWhenExpressions[tabLinkId].isEnabled; - } - - return true; - }); -} diff --git a/packages/smart-forms-renderer/src/components/Renderer/FormBodyTabbed.tsx b/packages/smart-forms-renderer/src/components/Renderer/FormBodyTabbed.tsx index 4c5f7aa85..4ce6333e3 100644 --- a/packages/smart-forms-renderer/src/components/Renderer/FormBodyTabbed.tsx +++ b/packages/smart-forms-renderer/src/components/Renderer/FormBodyTabbed.tsx @@ -23,7 +23,7 @@ import TabPanel from '@mui/lab/TabPanel'; import { getQrItemsIndex, mapQItemsIndex } from '../../utils/mapItem'; import GroupItem from '../FormComponents/GroupItem/GroupItem'; import { updateQrGroup } from '../../utils/qrItem'; -import FormBodyTabList from '../Tabs/FormBodyTabList'; +import FormBodyTabListWrapper from '../Tabs/FormBodyTabListWrapper'; import type { PropsWithQrItemChangeHandler } from '../../interfaces/renderProps.interface'; import useQuestionnaireStore from '../../stores/useQuestionnaireStore'; @@ -61,7 +61,7 @@ function FormBodyTabbed(props: FormBodyTabbedProps) { - + diff --git a/packages/smart-forms-renderer/src/components/Tabs/FormBodySingleTab.tsx b/packages/smart-forms-renderer/src/components/Tabs/FormBodySingleTab.tsx index a672da6b8..ef8433a92 100644 --- a/packages/smart-forms-renderer/src/components/Tabs/FormBodySingleTab.tsx +++ b/packages/smart-forms-renderer/src/components/Tabs/FormBodySingleTab.tsx @@ -15,7 +15,7 @@ * limitations under the License. */ -import React, { memo, useMemo } from 'react'; +import React, { memo } from 'react'; import Box from '@mui/material/Box'; import ListItemButton from '@mui/material/ListItemButton'; import ListItemText from '@mui/material/ListItemText'; @@ -23,45 +23,20 @@ import Typography from '@mui/material/Typography'; import useQuestionnaireStore from '../../stores/useQuestionnaireStore'; import type { QuestionnaireItem } from 'fhir/r4'; -import { getContextDisplays } from '../../utils/tabs'; -import ItemLabelText from '../FormComponents/ItemParts/ItemLabelText'; -import { isHidden } from '../../utils/qItem'; +import ContextDisplayItem from '../FormComponents/ItemParts/ContextDisplayItem'; interface FormBodySingleTabProps { - qItem: QuestionnaireItem; + contextDisplayItems: QuestionnaireItem[]; selected: boolean; tabLabel: string; listIndex: number; - completedTabsCollapsed: boolean; } const FormBodySingleTab = memo(function FormBodySingleTab(props: FormBodySingleTabProps) { - const { qItem, selected, tabLabel, listIndex, completedTabsCollapsed } = props; + const { contextDisplayItems, selected, tabLabel, listIndex } = props; - const enableWhenIsActivated = useQuestionnaireStore((state) => state.enableWhenIsActivated); - const enableWhenItems = useQuestionnaireStore((state) => state.enableWhenItems); - const enableWhenExpressions = useQuestionnaireStore((state) => state.enableWhenExpressions); const switchTab = useQuestionnaireStore((state) => state.switchTab); - const visibleContextDisplayItems = useMemo( - () => - getContextDisplays(qItem).filter( - (contextDisplayItem) => - !isHidden({ - questionnaireItem: contextDisplayItem, - enableWhenIsActivated, - enableWhenItems, - enableWhenExpressions - }) - ), - [enableWhenExpressions, enableWhenIsActivated, enableWhenItems, qItem] - ); - - const tabIsCompleted = visibleContextDisplayItems.find((item) => item.text === 'Complete'); - if (tabIsCompleted && completedTabsCollapsed) { - return null; - } - function handleTabClick() { switchTab(listIndex); window.scrollTo(0, 0); @@ -78,9 +53,9 @@ const FormBodySingleTab = memo(function FormBodySingleTab(props: FormBodySingleT {tabLabel} - {visibleContextDisplayItems.map((item) => ( - - ))} + {contextDisplayItems.map((item) => { + return ; + })} } diff --git a/packages/smart-forms-renderer/src/components/Tabs/FormBodyTabList.tsx b/packages/smart-forms-renderer/src/components/Tabs/FormBodyTabList.tsx index cea38c66b..03dc2e5b5 100644 --- a/packages/smart-forms-renderer/src/components/Tabs/FormBodyTabList.tsx +++ b/packages/smart-forms-renderer/src/components/Tabs/FormBodyTabList.tsx @@ -15,99 +15,70 @@ * limitations under the License. */ -import React, { memo, useState } from 'react'; -import Box from '@mui/material/Box'; -import Card from '@mui/material/Card'; +import React, { memo, useMemo } from 'react'; import Collapse from '@mui/material/Collapse'; -import { PrimarySelectableList } from '../Lists.styles'; import { TransitionGroup } from 'react-transition-group'; -import { isHidden } from '../../utils/qItem'; import { getShortText } from '../../utils/itemControl'; import type { QuestionnaireItem } from 'fhir/r4'; import FormBodySingleTab from './FormBodySingleTab'; import type { Tabs } from '../../interfaces/tab.interface'; import useQuestionnaireStore from '../../stores/useQuestionnaireStore'; -import { IconButton } from '@mui/material'; -import ExpandLess from '@mui/icons-material/ExpandLess'; -import ExpandMore from '@mui/icons-material/ExpandMore'; -import Typography from '@mui/material/Typography'; -import Divider from '@mui/material/Divider'; +import { getContextDisplays, isTabHidden } from '../../utils/tabs'; interface FormBodyTabListProps { - qFormItems: QuestionnaireItem[]; + topLevelItems: QuestionnaireItem[]; currentTabIndex: number; tabs: Tabs; + completedTabsCollapsed: boolean; } const FormBodyTabList = memo(function FormBodyTabList(props: FormBodyTabListProps) { - const { qFormItems, currentTabIndex, tabs } = props; - - const [completedTabsExpanded, setCompletedTabsExpanded] = useState(true); + const { topLevelItems, currentTabIndex, tabs, completedTabsCollapsed } = props; const enableWhenIsActivated = useQuestionnaireStore((state) => state.enableWhenIsActivated); const enableWhenItems = useQuestionnaireStore((state) => state.enableWhenItems); const enableWhenExpressions = useQuestionnaireStore((state) => state.enableWhenExpressions); - return ( - - - - - - Completed tabs {completedTabsExpanded ? 'shown' : 'hidden'} - - { - setCompletedTabsExpanded(!completedTabsExpanded); - }}> - {completedTabsExpanded ? ( - - ) : ( - - )} - - - + const allContextDisplayItems = useMemo( + () => topLevelItems.map((topLevelItem) => getContextDisplays(topLevelItem)), + [topLevelItems] + ); - - {qFormItems.map((qItem, i) => { - const isTab = !!tabs[qItem.linkId]; + return ( + + {topLevelItems.map((qItem, i) => { + const contextDisplayItems = allContextDisplayItems[i]; + const isTab = !!tabs[qItem.linkId]; - if ( - !isTab || - isHidden({ - questionnaireItem: qItem, - enableWhenIsActivated, - enableWhenItems, - enableWhenExpressions - }) - ) { - return null; - } + if ( + isTabHidden({ + qItem, + contextDisplayItems, + isTab, + enableWhenIsActivated, + enableWhenItems, + enableWhenExpressions, + completedTabsCollapsed + }) + ) { + return null; + } - const tabIsSelected = currentTabIndex.toString() === i.toString(); - const tabLabel = getShortText(qItem) ?? qItem.text ?? ''; + const tabIsSelected = currentTabIndex.toString() === i.toString(); + const tabLabel = getShortText(qItem) ?? qItem.text ?? ''; - return ( - - - - ); - })} - - - - + return ( + + + + ); + })} + ); }); diff --git a/packages/smart-forms-renderer/src/components/Tabs/FormBodyTabListWrapper.tsx b/packages/smart-forms-renderer/src/components/Tabs/FormBodyTabListWrapper.tsx new file mode 100644 index 000000000..10f03cd33 --- /dev/null +++ b/packages/smart-forms-renderer/src/components/Tabs/FormBodyTabListWrapper.tsx @@ -0,0 +1,80 @@ +/* + * 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 React, { memo, useState } from 'react'; +import Box from '@mui/material/Box'; +import Card from '@mui/material/Card'; +import { PrimarySelectableList } from '../Lists.styles'; +import type { QuestionnaireItem } from 'fhir/r4'; +import type { Tabs } from '../../interfaces/tab.interface'; +import { IconButton } from '@mui/material'; +import ExpandLess from '@mui/icons-material/ExpandLess'; +import ExpandMore from '@mui/icons-material/ExpandMore'; +import Typography from '@mui/material/Typography'; +import Divider from '@mui/material/Divider'; +import FormBodyTabList from './FormBodyTabList'; + +interface FormBodyTabListWrapperProps { + qFormItems: QuestionnaireItem[]; + currentTabIndex: number; + tabs: Tabs; +} + +const FormBodyTabListWrapper = memo(function FormBodyTabListWrapper( + props: FormBodyTabListWrapperProps +) { + const { qFormItems, currentTabIndex, tabs } = props; + + const [completedTabsExpanded, setCompletedTabsExpanded] = useState(true); + + return ( + + + + + + Completed tabs {completedTabsExpanded ? 'shown' : 'hidden'} + + { + setCompletedTabsExpanded(!completedTabsExpanded); + }}> + {completedTabsExpanded ? ( + + ) : ( + + )} + + + + + + + + ); +}); + +export default FormBodyTabListWrapper; diff --git a/packages/smart-forms-renderer/src/utils/tabs.ts b/packages/smart-forms-renderer/src/utils/tabs.ts index b307b3415..ddb1af9e4 100644 --- a/packages/smart-forms-renderer/src/utils/tabs.ts +++ b/packages/smart-forms-renderer/src/utils/tabs.ts @@ -19,7 +19,7 @@ import type { Tabs } from '../interfaces/tab.interface'; import type { EnableWhenExpression, EnableWhenItems } from '../interfaces/enableWhen.interface'; import type { Coding, QuestionnaireItem } from 'fhir/r4'; import { hasHiddenExtension, isSpecificItemControl } from './itemControl'; -import { isHiddenByEnableWhens } from './qItem'; +import { isHidden, isHiddenByEnableWhens } from './qItem'; export function getFirstVisibleTab( tabs: Tabs, @@ -253,3 +253,56 @@ export function getContextDisplays(item: QuestionnaireItem): QuestionnaireItem[] isSpecificItemControl(childItem, 'context-display') && childItem.type === 'display' ); } + +interface IsTabHiddenParams { + qItem: QuestionnaireItem; + contextDisplayItems: QuestionnaireItem[]; + isTab: boolean; + enableWhenIsActivated: boolean; + enableWhenItems: EnableWhenItems; + enableWhenExpressions: Record; + completedTabsCollapsed: boolean; +} + +export function isTabHidden(params: IsTabHiddenParams): boolean { + const { + qItem, + contextDisplayItems, + isTab, + enableWhenIsActivated, + enableWhenItems, + enableWhenExpressions, + completedTabsCollapsed + } = params; + + if ( + !isTab || + isHidden({ + questionnaireItem: qItem, + enableWhenIsActivated, + enableWhenItems, + enableWhenExpressions + }) + ) { + return true; + } + + if (completedTabsCollapsed) { + const completedDisplayItem = contextDisplayItems.find( + (contextDisplayItem) => contextDisplayItem.text === 'Complete' + ); + if ( + completedDisplayItem && + !isHidden({ + questionnaireItem: completedDisplayItem, + enableWhenIsActivated, + enableWhenItems, + enableWhenExpressions + }) + ) { + return true; + } + } + + return false; +}