From 070287670b4528d56e2289a55c8c8f0e6fa6e9b6 Mon Sep 17 00:00:00 2001 From: Riza Nafis <31271087+ryuuzake@users.noreply.github.com> Date: Fri, 2 Aug 2024 21:19:46 +0700 Subject: [PATCH] Fix pages component render logic (#3) * Changes color to string literal instead of custom palette * Rename FormBodyPage to FormTopLevelPage for top level renderer * Add Renderer Logic for Page Container * Add Renderer Logic for Page Container - Non Single Top Level * Changes Page Button design --- packages/smart-forms-renderer/package.json | 2 +- .../FormComponents/Button.styles.ts | 8 +- .../GroupItem/GroupItemView.tsx | 1 - .../GroupItem/PageButtonWrapper.tsx | 24 +- .../src/components/Renderer/BaseRenderer.tsx | 4 +- .../src/components/Renderer/FormBodyPage.tsx | 37 +- .../components/Renderer/FormTopLevelItem.tsx | 16 + .../components/Renderer/FormTopLevelPage.tsx | 70 +++ .../questionnaires/QItemControlGroup.ts | 549 ++++++++++++++++++ .../stories/sdc/ItemControlGroup.stories.tsx | 16 +- .../smart-forms-renderer/src/theme/palette.ts | 15 - .../smart-forms-renderer/src/utils/page.ts | 37 +- .../questionnaireStoreUtils/extractPages.ts | 17 +- 13 files changed, 746 insertions(+), 50 deletions(-) create mode 100644 packages/smart-forms-renderer/src/components/Renderer/FormTopLevelPage.tsx diff --git a/packages/smart-forms-renderer/package.json b/packages/smart-forms-renderer/package.json index a52464723..05fa88c07 100644 --- a/packages/smart-forms-renderer/package.json +++ b/packages/smart-forms-renderer/package.json @@ -1,6 +1,6 @@ { "name": "@aehrc/smart-forms-renderer", - "version": "0.37.0", + "version": "0.37.1", "description": "FHIR Structured Data Captured (SDC) rendering engine for Smart Forms", "main": "lib/index.js", "scripts": { diff --git a/packages/smart-forms-renderer/src/components/FormComponents/Button.styles.ts b/packages/smart-forms-renderer/src/components/FormComponents/Button.styles.ts index 6d3035b95..b31346e66 100644 --- a/packages/smart-forms-renderer/src/components/FormComponents/Button.styles.ts +++ b/packages/smart-forms-renderer/src/components/FormComponents/Button.styles.ts @@ -1,10 +1,10 @@ import { styled } from '@mui/material/styles'; import Fab from '@mui/material/Fab'; -export const StandardFab = styled(Fab)(({ theme }) => ({ - color: theme.palette.customButton.foreground, - background: theme.palette.customButton.background, +export const StandardFab = styled(Fab)(() => ({ + color: '#161C26', + background: '#0ABDC3', '&:hover': { - background: theme.palette.customButton.backgroundHover + background: '#08979C' } })); diff --git a/packages/smart-forms-renderer/src/components/FormComponents/GroupItem/GroupItemView.tsx b/packages/smart-forms-renderer/src/components/FormComponents/GroupItem/GroupItemView.tsx index a7e73aa74..920cdffdb 100644 --- a/packages/smart-forms-renderer/src/components/FormComponents/GroupItem/GroupItemView.tsx +++ b/packages/smart-forms-renderer/src/components/FormComponents/GroupItem/GroupItemView.tsx @@ -77,7 +77,6 @@ function GroupItemView(props: GroupItemViewProps) { onQrItemChange, onQrRepeatGroupChange } = props; - console.log({ pages, currentPageIndex }); const readOnly = useReadOnly(qItem, parentIsReadOnly); diff --git a/packages/smart-forms-renderer/src/components/FormComponents/GroupItem/PageButtonWrapper.tsx b/packages/smart-forms-renderer/src/components/FormComponents/GroupItem/PageButtonWrapper.tsx index 0d55972d5..4ddcfd788 100644 --- a/packages/smart-forms-renderer/src/components/FormComponents/GroupItem/PageButtonWrapper.tsx +++ b/packages/smart-forms-renderer/src/components/FormComponents/GroupItem/PageButtonWrapper.tsx @@ -1,5 +1,6 @@ import React, { memo } from 'react'; import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; import type { Pages } from '../../../interfaces/page.interface'; import { useQuestionnaireStore } from '../../../stores'; import NextPageButton from './NextPageButton'; @@ -57,20 +58,17 @@ const PageButtonsWrapper = memo(function PageButtonsWrapper(props: PageButtonsWr const pageButtonsDisabled = numOfVisiblePages <= 1; return ( - - {previousPageButtonHidden ? null : ( - - )} + - {nextPageButtonHidden ? null : ( - - )} + {`${currentPageIndex + 1} / ${numOfVisiblePages}`} + + ); }); diff --git a/packages/smart-forms-renderer/src/components/Renderer/BaseRenderer.tsx b/packages/smart-forms-renderer/src/components/Renderer/BaseRenderer.tsx index b2514e458..6abe9c68a 100644 --- a/packages/smart-forms-renderer/src/components/Renderer/BaseRenderer.tsx +++ b/packages/smart-forms-renderer/src/components/Renderer/BaseRenderer.tsx @@ -26,7 +26,7 @@ import { getQrItemsIndex, mapQItemsIndex } from '../../utils/mapItem'; import { updateQrItemsInGroup } from '../../utils/qrItem'; import { everyIsPages } from '../../utils/page'; import type { QrRepeatGroup } from '../../interfaces/repeatGroup.interface'; -import FormBodyPage from './FormBodyPage'; +import FormTopLevelPage from './FormTopLevelPage'; /** * Main component of the form-rendering engine. @@ -82,7 +82,7 @@ function BaseRenderer() { return ( - = useMemo( + () => mapQItemsIndex(topLevelQItem), + [topLevelQItem] + ); + + const nonNullTopLevelQRItem = topLevelQRItem ?? createEmptyQrGroup(topLevelQItem); + + const qItems = topLevelQItem.item; + const qrItems = nonNullTopLevelQRItem.item; + + function handleQrGroupChange(qrItem: QuestionnaireResponseItem) { + updateQrItemsInGroup(qrItem, null, nonNullTopLevelQRItem, indexMap); + onQrItemChange(nonNullTopLevelQRItem); + } + + if (!qItems || !qrItems) { + return <>Unable to load form; + } + + const qrItemsByIndex = getQrItemsIndex(qItems, qrItems, indexMap); + return ( - {topLevelQItems.map((qItem, i) => { - const qrItem = topLevelQRItems[i]; + {qItems.map((qItem, i) => { + const qrItem = qrItemsByIndex[i]; const isNotRepeatGroup = !Array.isArray(qrItem); const isPage = !!pages[qItem.linkId]; @@ -56,7 +79,7 @@ function FormBodyPage(props: FormBodyPageProps) { pages={pages} currentPageIndex={currentPage} parentIsReadOnly={parentIsReadOnly} - onQrItemChange={onQrItemChange} + onQrItemChange={handleQrGroupChange} /> ); diff --git a/packages/smart-forms-renderer/src/components/Renderer/FormTopLevelItem.tsx b/packages/smart-forms-renderer/src/components/Renderer/FormTopLevelItem.tsx index 2abdd1cd5..19cfc0ec2 100644 --- a/packages/smart-forms-renderer/src/components/Renderer/FormTopLevelItem.tsx +++ b/packages/smart-forms-renderer/src/components/Renderer/FormTopLevelItem.tsx @@ -20,6 +20,7 @@ import type { QuestionnaireItem, QuestionnaireResponseItem } from 'fhir/r4'; import FormBodyTabbed from './FormBodyTabbed'; import FormBodyPage from './FormBodyPage'; import { containsTabs, isTabContainer } from '../../utils/tabs'; +import { containsPages, isPage } from '../../utils/page'; import GroupItem from '../FormComponents/GroupItem/GroupItem'; import SingleItem from '../FormComponents/SingleItem/SingleItem'; import type { @@ -54,6 +55,9 @@ function FormTopLevelItem(props: FormTopLevelItemProps) { const itemIsTabContainer = isTabContainer(topLevelQItem); const itemContainsTabs = containsTabs(topLevelQItem); + const itemIsPageContainer = isPage(topLevelQItem); + const itemContainsPages = containsPages(topLevelQItem); + const isTablet = useResponsive('up', 'md'); const itemIsGroup = topLevelQItem.type === 'group'; @@ -107,6 +111,18 @@ function FormTopLevelItem(props: FormTopLevelItemProps) { ); } + if (itemContainsPages || itemIsPageContainer) { + return ( + + ); + } + // If form is untabbed, it is rendered as a regular group if (itemIsGroup) { return ( diff --git a/packages/smart-forms-renderer/src/components/Renderer/FormTopLevelPage.tsx b/packages/smart-forms-renderer/src/components/Renderer/FormTopLevelPage.tsx new file mode 100644 index 000000000..b2ae07b32 --- /dev/null +++ b/packages/smart-forms-renderer/src/components/Renderer/FormTopLevelPage.tsx @@ -0,0 +1,70 @@ +import React from 'react'; +import Grid from '@mui/material/Grid'; +import type { QuestionnaireItem, QuestionnaireResponseItem } from 'fhir/r4'; +import TabContext from '@mui/lab/TabContext'; +import TabPanel from '@mui/lab/TabPanel'; +import GroupItem from '../FormComponents/GroupItem/GroupItem'; +import type { + PropsWithParentIsReadOnlyAttribute, + PropsWithQrItemChangeHandler +} from '../../interfaces/renderProps.interface'; +import { useQuestionnaireStore } from '../../stores'; + +interface FormTopLevelPageProps + extends PropsWithQrItemChangeHandler, + PropsWithParentIsReadOnlyAttribute { + topLevelQItems: QuestionnaireItem[]; + topLevelQRItems: (QuestionnaireResponseItem | QuestionnaireResponseItem[] | undefined)[]; +} + +function FormTopLevelPage(props: FormTopLevelPageProps) { + const { topLevelQItems, topLevelQRItems, parentIsReadOnly, onQrItemChange } = props; + + const pages = useQuestionnaireStore.use.pages(); + const currentPage = useQuestionnaireStore.use.currentPageIndex(); + + return ( + + + + {topLevelQItems.map((qItem, i) => { + const qrItem = topLevelQRItems[i]; + + const isNotRepeatGroup = !Array.isArray(qrItem); + const isPage = !!pages[qItem.linkId]; + + if (!isPage || !isNotRepeatGroup) { + // Something has gone horribly wrong + return null; + } + + const isRepeated = qItem.repeats ?? false; + const pageIsMarkedAsComplete = pages[qItem.linkId].isComplete ?? false; + + return ( + + + + ); + })} + + + + ); +} + +export default FormTopLevelPage; diff --git a/packages/smart-forms-renderer/src/stories/assets/questionnaires/QItemControlGroup.ts b/packages/smart-forms-renderer/src/stories/assets/questionnaires/QItemControlGroup.ts index 955140057..911cae30d 100644 --- a/packages/smart-forms-renderer/src/stories/assets/questionnaires/QItemControlGroup.ts +++ b/packages/smart-forms-renderer/src/stories/assets/questionnaires/QItemControlGroup.ts @@ -755,3 +755,552 @@ export const qItemControlGroupPage: Questionnaire = { } ] }; + +export const qItemControlGroupPageContainer: Questionnaire = { + resourceType: 'Questionnaire', + id: 'ItemControlGroupPage', + name: 'ItemControlGroupPage', + title: 'Item Control Group Page', + version: '0.1.0', + status: 'draft', + publisher: 'AEHRC CSIRO', + date: '2024-07-24', + url: 'https://smartforms.csiro.au/docs/advanced/control/item-control-group-page', + item: [ + { + extension: [ + { + url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl', + valueCodeableConcept: { + coding: [ + { + system: 'http://hl7.org/fhir/questionnaire-item-control', + version: '1.0.0', + code: 'page' + } + ] + } + } + ], + linkId: 'page-container', + type: 'group', + repeats: false, + item: [ + { + linkId: 'page-about-health-check', + text: 'About the health check', + type: 'group', + repeats: false, + item: [ + { + linkId: 'health-check-eligible', + text: 'Eligible for health check', + type: 'boolean', + repeats: false + }, + { + linkId: 'health-check-in-progress', + text: 'Health check already in progress?', + type: 'boolean', + repeats: false + }, + { + linkId: 'health-check-last-completed', + text: 'Date of last completed health check', + type: 'date', + repeats: false + }, + { + linkId: 'health-check-this-commenced', + text: 'Date and time this health check commenced', + type: 'dateTime', + repeats: false + } + ] + }, + { + extension: [ + { + url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-shortText', + valueString: 'Current priorities' + } + ], + linkId: 'page-current-priorities', + text: 'Current health/patient priorities', + type: 'group', + repeats: false, + item: [ + { + extension: [ + { + url: 'http://hl7.org/fhir/StructureDefinition/entryFormat', + valueString: 'Enter details' + } + ], + linkId: 'current-priorities-important-things', + text: 'What are the important things for you in this health check today?', + type: 'text', + repeats: false + }, + { + extension: [ + { + url: 'http://hl7.org/fhir/StructureDefinition/entryFormat', + valueString: 'Enter details' + } + ], + linkId: 'current-priorities-worried-things', + text: 'Is there anything you are worried about?', + type: 'text', + repeats: false + } + ] + } + ] + } + ] +}; + +export const qItemControlGroupPageNonTopLevelPageContainer: Questionnaire = { + resourceType: 'Questionnaire', + id: 'bit-of-everything', + status: 'draft', + title: 'A bit of everything', + subjectType: ['Patient', 'Person', 'Practitioner'], + item: [ + { + linkId: 'summary', + type: 'display', + text: 'This questionnaire is a bit of everything. It contains virtually every question type we might need in 80% of most surveys.' + }, + { + extension: [ + { + url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl', + valueCodeableConcept: { + coding: [ + { + system: 'http://hl7.org/fhir/questionnaire-item-control', + version: '1.0.0', + code: 'page' + } + ] + } + } + ], + linkId: 'page-container', + type: 'group', + repeats: false, + item: [ + { + linkId: 'group-basic1', + type: 'group', + text: 'A group of basic questions 1', + item: [ + { + linkId: 'radio-choice1', + type: 'choice', + text: 'A question with radio button choices', + answerValueSet: 'http://hl7.org/fhir/ValueSet/yesnodontknow', + extension: [ + { + url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl', + valueCodeableConcept: { + coding: [ + { + system: 'http://hl7.org/fhir/questionnaire-item-control', + code: 'radio-button' + } + ] + } + }, + { + url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-choiceOrientation', + valueCode: 'vertical' + } + ] + }, + { + linkId: 'dropdown-choice1', + type: 'choice', + text: 'A question with dropdown choices', + answerValueSet: 'http://hl7.org/fhir/ValueSet/yesnodontknow', + extension: [ + { + url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl', + valueCodeableConcept: { + coding: [ + { + system: 'http://hl7.org/fhir/questionnaire-item-control', + code: 'drop-down' + } + ] + } + } + ] + }, + { + linkId: 'checkbox-choice1', + type: 'choice', + text: 'A question with checkbox choices', + answerValueSet: 'http://hl7.org/fhir/ValueSet/yesnodontknow', + repeats: true, + extension: [ + { + url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl', + valueCodeableConcept: { + coding: [ + { + system: 'http://hl7.org/fhir/questionnaire-item-control', + code: 'check-box' + } + ] + } + }, + { + url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-choiceOrientation', + valueCode: 'vertical' + } + ] + } + ] + }, + { + linkId: 'group-basic2', + type: 'group', + text: 'A group of basic questions 2', + item: [ + { + linkId: 'radio-choice2', + type: 'choice', + text: 'A question with radio button choices', + answerValueSet: 'http://hl7.org/fhir/ValueSet/yesnodontknow', + extension: [ + { + url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl', + valueCodeableConcept: { + coding: [ + { + system: 'http://hl7.org/fhir/questionnaire-item-control', + code: 'radio-button' + } + ] + } + }, + { + url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-choiceOrientation', + valueCode: 'vertical' + } + ] + }, + { + linkId: 'dropdown-choice2', + type: 'choice', + text: 'A question with dropdown choices', + answerValueSet: 'http://hl7.org/fhir/ValueSet/yesnodontknow', + extension: [ + { + url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl', + valueCodeableConcept: { + coding: [ + { + system: 'http://hl7.org/fhir/questionnaire-item-control', + code: 'drop-down' + } + ] + } + } + ] + }, + { + linkId: 'checkbox-choice2', + type: 'choice', + text: 'A question with checkbox choices', + answerValueSet: 'http://hl7.org/fhir/ValueSet/yesnodontknow', + repeats: true, + extension: [ + { + url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl', + valueCodeableConcept: { + coding: [ + { + system: 'http://hl7.org/fhir/questionnaire-item-control', + code: 'check-box' + } + ] + } + }, + { + url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-choiceOrientation', + valueCode: 'vertical' + } + ] + } + ] + } + ] + }, + { + linkId: 'group-conditional', + type: 'group', + text: 'A group of conditional logic workflow', + item: [ + { + linkId: 'condition', + text: 'Try to insert a positive/negative number', + type: 'decimal', + initial: [ + { + valueInteger: 0 + } + ], + extension: [ + { + url: 'http://hl7.org/fhir/StructureDefinition/minValue', + valueInteger: -10 + }, + { + url: 'http://hl7.org/fhir/StructureDefinition/maxValue', + valueInteger: 10 + } + ] + }, + { + linkId: 'conditional-gt5', + type: 'display', + text: 'An item that appears when the slider entry > 0', + enableWhen: [ + { + question: 'condition', + operator: '>', + answerDecimal: 0 + } + ] + }, + { + linkId: 'conditional-lt5', + type: 'display', + text: 'An item that appears when the slider entry < 0', + enableWhen: [ + { + question: 'condition', + operator: '<', + answerDecimal: 0 + } + ] + } + ] + }, + { + linkId: 'autofill', + type: 'group', + text: 'A group of questions with autofill options', + item: [ + { + linkId: 'diagnosis', + text: 'Diagnosis', + type: 'open-choice', + repeats: true, + answerValueSet: 'https://clinicaltables.nlm.nih.gov/fhir/R4/ValueSet/icd10cm', + extension: [ + { + url: 'http://hl7.org/fhir/StructureDefinition/terminology-server', + valueUrl: 'https://clinicaltables.nlm.nih.gov/fhir/R4' + }, + { + url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl', + valueCodeableConcept: { + coding: [ + { + system: 'http://hl7.org/fhir/questionnaire-item-control', + code: 'autocomplete' + } + ] + } + } + ] + }, + { + linkId: 'medications_table', + text: 'Medications', + type: 'group', + repeats: true, + extension: [ + { + url: 'http://hl7.org/fhir/StructureDefinition/terminology-server', + valueUrl: 'https://clinicaltables.nlm.nih.gov/fhir/R4' + }, + { + url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl', + valueCodeableConcept: { + coding: [ + { + system: 'http://hl7.org/fhir/questionnaire-item-control', + code: 'gtable' + } + ] + } + }, + { + url: 'http://hl7.org/fhir/StructureDefinition/variable', + valueExpression: { + name: 'strengthFormLookup', + language: 'application/x-fhir-query', + expression: + "https://clinicaltables.nlm.nih.gov/fhir/R4/CodeSystem/$lookup?system=https://clinicaltables.nlm.nih.gov/fhir/CodeSystem/rxterms&code={{item.where(linkId='medication').answer.valueCoding.code}}&property=STRENGTHS_AND_FORMS" + } + } + ], + item: [ + { + linkId: 'medication', + text: 'Medication Name', + type: 'choice', + answerValueSet: 'https://clinicaltables.nlm.nih.gov/fhir/R4/ValueSet/rxterms', + extension: [ + { + url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl', + valueCodeableConcept: { + coding: [ + { + system: 'http://hl7.org/fhir/questionnaire-item-control', + code: 'autocomplete', + display: 'Auto-complete' + } + ], + text: 'Auto-complete' + } + } + ] + }, + { + linkId: 'strength', + text: 'Strength', + type: 'open-choice', + extension: [ + { + url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-answerExpression', + valueExpression: { + language: 'text/fhirpath', + expression: + "%strengthFormLookup.parameter.where(name='property' and part.where(name='code' and value='STRENGTHS_AND_FORMS').exists()).part.where(name='value').value" + } + } + ] + } + ] + } + ] + }, + { + linkId: 'scoring', + type: 'group', + text: 'A group of questions with automatic scoring', + item: [ + { + linkId: 'question-table', + text: 'A table with questions in its rows and choices in its columns', + type: 'group', + item: [ + { + linkId: 'question-1', + type: 'choice', + text: 'What is it?', + answerOption: [ + { + valueCoding: { + code: '0', + display: 'This' + } + }, + { + valueCoding: { + code: '1', + display: 'That' + } + } + ] + }, + { + linkId: 'question-2', + type: 'choice', + text: 'What is it again?', + answerOption: [ + { + valueCoding: { + code: '0', + display: 'This' + } + }, + { + valueCoding: { + code: '1', + display: 'That' + } + } + ] + } + ], + extension: [ + { + url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl', + valueCodeableConcept: { + coding: [ + { + system: 'http://hl7.org/fhir/questionnaire-item-control', + code: 'table' + } + ] + } + } + ] + }, + { + linkId: 'scored-items', + type: 'decimal', + text: 'Score', + extension: [ + { + url: 'http://hl7.org/fhir/StructureDefinition/variable', + valueExpression: { + name: 'answer1', + language: 'text/fhirpath', + expression: + "%resource.item.where(linkId='scoring').item.where(linkId='question-table').item.where(linkId='question-1').answer.valueCoding.code.toDecimal()" + } + }, + { + url: 'http://hl7.org/fhir/StructureDefinition/variable', + valueExpression: { + name: 'answer2', + language: 'text/fhirpath', + expression: + "%resource.item.where(linkId='scoring').item.where(linkId='question-table').item.where(linkId='question-2').answer.valueCoding.code.toDecimal()" + } + }, + { + url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-calculatedExpression', + valueExpression: { + language: 'text/fhirpath', + expression: '%answer1 + %answer2' + } + } + ], + enableWhen: [ + { + question: 'question-1', + operator: 'exists', + answerBoolean: true + }, + { + question: 'question-2', + operator: 'exists', + answerBoolean: true + } + ], + enableBehavior: 'all' + } + ] + } + ] +}; diff --git a/packages/smart-forms-renderer/src/stories/sdc/ItemControlGroup.stories.tsx b/packages/smart-forms-renderer/src/stories/sdc/ItemControlGroup.stories.tsx index e7ceeb73b..78580fada 100644 --- a/packages/smart-forms-renderer/src/stories/sdc/ItemControlGroup.stories.tsx +++ b/packages/smart-forms-renderer/src/stories/sdc/ItemControlGroup.stories.tsx @@ -22,7 +22,9 @@ import { qItemControlGroupGridMultiRow, qItemControlGroupGridSingleRow, qItemControlGroupGTable, - qItemControlGroupPage + qItemControlGroupPage, + qItemControlGroupPageContainer, + qItemControlGroupPageNonTopLevelPageContainer } from '../assets/questionnaires'; // More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction#default-export @@ -67,3 +69,15 @@ export const Page: Story = { questionnaire: qItemControlGroupPage } }; + +export const PageContainer: Story = { + args: { + questionnaire: qItemControlGroupPageContainer + } +}; + +export const PageContainerNonSingleTopLevel: Story = { + args: { + questionnaire: qItemControlGroupPageNonTopLevelPageContainer + } +}; diff --git a/packages/smart-forms-renderer/src/theme/palette.ts b/packages/smart-forms-renderer/src/theme/palette.ts index 5393ab101..941e9ccd4 100644 --- a/packages/smart-forms-renderer/src/theme/palette.ts +++ b/packages/smart-forms-renderer/src/theme/palette.ts @@ -31,11 +31,6 @@ declare module '@mui/material/styles' { customBackground: { neutral: string; }; - customButton: { - background: string; - backgroundHover: string; - foreground: string; - }; } // noinspection JSUnusedGlobalSymbols @@ -49,11 +44,6 @@ declare module '@mui/material/styles' { customBackground?: { neutral: string; }; - customButton?: { - background: string; - backgroundHover: string; - foreground: string; - }; } } @@ -94,11 +84,6 @@ const palette: PaletteOptions = { customBackground: { neutral: '#F4F6F8' }, - customButton: { - background: '#0ABDC3', - backgroundHover: '#08979C', - foreground: '#161C26' - }, action: { active: grey['600'], hover: alpha(grey['500'], 0.08), diff --git a/packages/smart-forms-renderer/src/utils/page.ts b/packages/smart-forms-renderer/src/utils/page.ts index d16360b2e..343dfde26 100644 --- a/packages/smart-forms-renderer/src/utils/page.ts +++ b/packages/smart-forms-renderer/src/utils/page.ts @@ -36,17 +36,43 @@ export function getFirstVisiblePage( } /** - * Checks if any of the items in a qItem array is a page item - * Returns true if there is at least one page item + * Checks if all of the items in a qItem array is a page item + * Returns true if all items is page item + * Returns false if only have one item * * @author Riza Nafis */ export function everyIsPages(topLevelQItem: QuestionnaireItem[] | undefined): boolean { if (!topLevelQItem) return false; + if (isPageContainer(topLevelQItem)) return false; + return topLevelQItem.every((i: QuestionnaireItem) => isPage(i)); } +export function isPageContainer(topLevelQItem: QuestionnaireItem[] | undefined): boolean { + const anyPage = topLevelQItem?.filter(isPage); + + if (!anyPage) return false; + + return anyPage.some((page) => page.item?.every((i) => i.type === 'group') || false); +} + +/** + * Checks if any of the items in a qItem array is a page item + * Returns true if there is at least one page item + * + * @author Riza Nafis + */ +export function containsPages(topLevelQItem: QuestionnaireItem): boolean { + if (!topLevelQItem.item) { + return false; + } + + const pages = topLevelQItem.item.filter((i) => isPage(i)); + return pages.length > 0; +} + /** * Check if a qItem is a page item * @@ -61,10 +87,13 @@ export function isPage(item: QuestionnaireItem) { * * @author Riza Nafis */ -export function constructPagesWithProperties(qItems: QuestionnaireItem[] | undefined): Pages { +export function constructPagesWithProperties( + qItems: QuestionnaireItem[] | undefined, + hasPageContainer: boolean +): Pages { if (!qItems) return {}; - const qItemPages = qItems.filter(isPage); + const qItemPages = hasPageContainer ? qItems : qItems.filter(isPage); const pages: Pages = {}; for (const [i, qItem] of qItemPages.entries()) { diff --git a/packages/smart-forms-renderer/src/utils/questionnaireStoreUtils/extractPages.ts b/packages/smart-forms-renderer/src/utils/questionnaireStoreUtils/extractPages.ts index 3f780328d..d6a29f60e 100644 --- a/packages/smart-forms-renderer/src/utils/questionnaireStoreUtils/extractPages.ts +++ b/packages/smart-forms-renderer/src/utils/questionnaireStoreUtils/extractPages.ts @@ -1,11 +1,24 @@ import type { Questionnaire } from 'fhir/r4'; import type { Pages } from '../../interfaces/page.interface'; -import { constructPagesWithProperties } from '../page'; +import { constructPagesWithProperties, isPage, isPageContainer } from '../page'; export function extractPages(questionnaire: Questionnaire): Pages { if (!questionnaire.item || questionnaire.item.length === 0) { return {}; } - return constructPagesWithProperties(questionnaire.item); + if (!isPageContainer(questionnaire.item)) { + return constructPagesWithProperties(questionnaire.item, false); + } + + let totalPages = {}; + for (const topLevelItem of questionnaire.item) { + const items = topLevelItem.item; + const topLevelItemIsPageContainer = isPage(topLevelItem); + + const pages = constructPagesWithProperties(items, topLevelItemIsPageContainer); + totalPages = { ...totalPages, ...pages }; + } + + return totalPages; }