@aehrc/smart-forms-renderer 0.36.0 → 0.37.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.
Files changed (106) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/lib/components/FormComponents/Button.styles.d.ts +4 -0
  3. package/lib/components/FormComponents/Button.styles.js +10 -0
  4. package/lib/components/FormComponents/Button.styles.js.map +1 -0
  5. package/lib/components/FormComponents/GroupItem/GroupHeading.d.ts +1 -0
  6. package/lib/components/FormComponents/GroupItem/GroupHeading.js +3 -2
  7. package/lib/components/FormComponents/GroupItem/GroupHeading.js.map +1 -1
  8. package/lib/components/FormComponents/GroupItem/GroupItem.d.ts +4 -0
  9. package/lib/components/FormComponents/GroupItem/GroupItem.js +3 -3
  10. package/lib/components/FormComponents/GroupItem/GroupItem.js.map +1 -1
  11. package/lib/components/FormComponents/GroupItem/GroupItemView.d.ts +4 -0
  12. package/lib/components/FormComponents/GroupItem/GroupItemView.js +8 -5
  13. package/lib/components/FormComponents/GroupItem/GroupItemView.js.map +1 -1
  14. package/lib/components/FormComponents/GroupItem/NextPageButton.d.ts +7 -0
  15. package/lib/components/FormComponents/GroupItem/NextPageButton.js +26 -0
  16. package/lib/components/FormComponents/GroupItem/NextPageButton.js.map +1 -0
  17. package/lib/components/FormComponents/GroupItem/PageButtonWrapper.d.ts +8 -0
  18. package/lib/components/FormComponents/GroupItem/PageButtonWrapper.js +46 -0
  19. package/lib/components/FormComponents/GroupItem/PageButtonWrapper.js.map +1 -0
  20. package/lib/components/FormComponents/GroupItem/PreviousPageButton.d.ts +7 -0
  21. package/lib/components/FormComponents/GroupItem/PreviousPageButton.js +26 -0
  22. package/lib/components/FormComponents/GroupItem/PreviousPageButton.js.map +1 -0
  23. package/lib/components/Renderer/BaseRenderer.js +8 -0
  24. package/lib/components/Renderer/BaseRenderer.js.map +1 -1
  25. package/lib/components/Renderer/FormBodyPage.d.ts +9 -0
  26. package/lib/components/Renderer/FormBodyPage.js +43 -0
  27. package/lib/components/Renderer/FormBodyPage.js.map +1 -0
  28. package/lib/components/Renderer/FormTopLevelItem.js +7 -0
  29. package/lib/components/Renderer/FormTopLevelItem.js.map +1 -1
  30. package/lib/components/Renderer/FormTopLevelPage.d.ts +9 -0
  31. package/lib/components/Renderer/FormTopLevelPage.js +29 -0
  32. package/lib/components/Renderer/FormTopLevelPage.js.map +1 -0
  33. package/lib/hooks/useCodingCalculatedExpression.js +11 -0
  34. package/lib/hooks/useCodingCalculatedExpression.js.map +1 -1
  35. package/lib/hooks/useNextAndPreviousVisiblePages.d.ts +7 -0
  36. package/lib/hooks/useNextAndPreviousVisiblePages.js +47 -0
  37. package/lib/hooks/useNextAndPreviousVisiblePages.js.map +1 -0
  38. package/lib/interfaces/page.interface.d.ts +16 -0
  39. package/lib/interfaces/page.interface.js +2 -0
  40. package/lib/interfaces/page.interface.js.map +1 -0
  41. package/lib/interfaces/questionnaireStore.interface.d.ts +2 -0
  42. package/lib/stores/questionnaireStore.d.ts +13 -0
  43. package/lib/stores/questionnaireStore.js +19 -3
  44. package/lib/stores/questionnaireStore.js.map +1 -1
  45. package/lib/utils/calculatedExpression.js +1 -1
  46. package/lib/utils/calculatedExpression.js.map +1 -1
  47. package/lib/utils/fhirpath.js +0 -4
  48. package/lib/utils/fhirpath.js.map +1 -1
  49. package/lib/utils/initialise.d.ts +3 -0
  50. package/lib/utils/initialise.js +6 -1
  51. package/lib/utils/initialise.js.map +1 -1
  52. package/lib/utils/page.d.ts +43 -0
  53. package/lib/utils/page.js +101 -0
  54. package/lib/utils/page.js.map +1 -0
  55. package/lib/utils/questionnaireStoreUtils/createQuestionaireModel.js +4 -0
  56. package/lib/utils/questionnaireStoreUtils/createQuestionaireModel.js.map +1 -1
  57. package/lib/utils/questionnaireStoreUtils/extractPages.d.ts +3 -0
  58. package/lib/utils/questionnaireStoreUtils/extractPages.js +18 -0
  59. package/lib/utils/questionnaireStoreUtils/extractPages.js.map +1 -0
  60. package/package.json +4 -4
  61. package/src/components/FormComponents/Button.styles.ts +10 -0
  62. package/src/components/FormComponents/GroupItem/GroupHeading.tsx +5 -3
  63. package/src/components/FormComponents/GroupItem/GroupItem.tsx +11 -1
  64. package/src/components/FormComponents/GroupItem/GroupItemView.tsx +12 -0
  65. package/src/components/FormComponents/GroupItem/NextPageButton.tsx +37 -0
  66. package/src/components/FormComponents/GroupItem/PageButtonWrapper.tsx +78 -0
  67. package/src/components/FormComponents/GroupItem/PreviousPageButton.tsx +41 -0
  68. package/src/components/Renderer/BaseRenderer.tsx +21 -0
  69. package/src/components/Renderer/FormBodyPage.tsx +93 -0
  70. package/src/components/Renderer/FormTopLevelItem.tsx +17 -0
  71. package/src/components/Renderer/FormTopLevelPage.tsx +70 -0
  72. package/src/hooks/useCodingCalculatedExpression.ts +14 -1
  73. package/src/hooks/useNextAndPreviousVisiblePages.ts +69 -0
  74. package/src/interfaces/page.interface.ts +13 -0
  75. package/src/interfaces/questionnaireStore.interface.ts +2 -0
  76. package/src/stores/questionnaireStore.ts +33 -2
  77. package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreAllergyIntolerance.json +209 -0
  78. package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreCondition.json +220 -0
  79. package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreEncounter.json +299 -0
  80. package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreImmunization.json +175 -0
  81. package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreMedicationRequest.json +229 -0
  82. package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreObservationBP.json +359 -0
  83. package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreObservationBodyHeight.json +195 -0
  84. package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreObservationBodyWeight.json +195 -0
  85. package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreObservationHeartRate.json +195 -0
  86. package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreObservationSmokingStatus.json +174 -0
  87. package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCorePatient.json +495 -0
  88. package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCorePractitioner.json +139 -0
  89. package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCorePractitionerRole.json +216 -0
  90. package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreProcedure.json +199 -0
  91. package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreRespirationRate.json +195 -0
  92. package/src/stories/assets/questionnaires/AuCoreTestingJson/AuCoreWaistCircumference.json +195 -0
  93. package/src/stories/assets/questionnaires/QAuCoreTesting.ts +3796 -0
  94. package/src/stories/assets/questionnaires/QItemControlGroup.ts +673 -0
  95. package/src/stories/assets/questionnaires/QPrePopTester.ts +266 -71
  96. package/src/stories/sdc/ItemControlGroup.stories.tsx +22 -1
  97. package/src/stories/testing/AuCoreTester.stories.tsx +196 -0
  98. package/src/utils/calculatedExpression.ts +1 -1
  99. package/src/utils/fhirpath.ts +0 -5
  100. package/src/utils/initialise.ts +11 -0
  101. package/src/utils/page.ts +134 -0
  102. package/src/utils/questionnaireStoreUtils/createQuestionaireModel.ts +5 -0
  103. package/src/utils/questionnaireStoreUtils/extractPages.ts +24 -0
  104. package/stats.html +0 -4842
  105. package/stats1.html +0 -4842
  106. package/stats3.html +0 -4842
@@ -18,7 +18,9 @@
18
18
  import React from 'react';
19
19
  import type { QuestionnaireItem, QuestionnaireResponseItem } from 'fhir/r4';
20
20
  import FormBodyTabbed from './FormBodyTabbed';
21
+ import FormBodyPage from './FormBodyPage';
21
22
  import { containsTabs, isTabContainer } from '../../utils/tabs';
23
+ import { containsPages, isPage } from '../../utils/page';
22
24
  import GroupItem from '../FormComponents/GroupItem/GroupItem';
23
25
  import SingleItem from '../FormComponents/SingleItem/SingleItem';
24
26
  import type {
@@ -53,6 +55,9 @@ function FormTopLevelItem(props: FormTopLevelItemProps) {
53
55
  const itemIsTabContainer = isTabContainer(topLevelQItem);
54
56
  const itemContainsTabs = containsTabs(topLevelQItem);
55
57
 
58
+ const itemIsPageContainer = isPage(topLevelQItem);
59
+ const itemContainsPages = containsPages(topLevelQItem);
60
+
56
61
  const isTablet = useResponsive('up', 'md');
57
62
 
58
63
  const itemIsGroup = topLevelQItem.type === 'group';
@@ -106,6 +111,18 @@ function FormTopLevelItem(props: FormTopLevelItemProps) {
106
111
  );
107
112
  }
108
113
 
114
+ if (itemContainsPages || itemIsPageContainer) {
115
+ return (
116
+ <FormBodyPage
117
+ key={topLevelQItem.linkId}
118
+ topLevelQItem={topLevelQItem}
119
+ topLevelQRItem={topLevelQRItem}
120
+ parentIsReadOnly={readOnly}
121
+ onQrItemChange={onQrItemChange}
122
+ />
123
+ );
124
+ }
125
+
109
126
  // If form is untabbed, it is rendered as a regular group
110
127
  if (itemIsGroup) {
111
128
  return (
@@ -0,0 +1,70 @@
1
+ import React 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
+
13
+ interface FormTopLevelPageProps
14
+ extends PropsWithQrItemChangeHandler,
15
+ PropsWithParentIsReadOnlyAttribute {
16
+ topLevelQItems: QuestionnaireItem[];
17
+ topLevelQRItems: (QuestionnaireResponseItem | QuestionnaireResponseItem[] | undefined)[];
18
+ }
19
+
20
+ function FormTopLevelPage(props: FormTopLevelPageProps) {
21
+ const { topLevelQItems, topLevelQRItems, parentIsReadOnly, onQrItemChange } = props;
22
+
23
+ const pages = useQuestionnaireStore.use.pages();
24
+ const currentPage = useQuestionnaireStore.use.currentPageIndex();
25
+
26
+ return (
27
+ <Grid container spacing={1.5}>
28
+ <TabContext value={currentPage.toString()}>
29
+ <Grid item xs={12} md={12} lg={12}>
30
+ {topLevelQItems.map((qItem, i) => {
31
+ const qrItem = topLevelQRItems[i];
32
+
33
+ const isNotRepeatGroup = !Array.isArray(qrItem);
34
+ const isPage = !!pages[qItem.linkId];
35
+
36
+ if (!isPage || !isNotRepeatGroup) {
37
+ // Something has gone horribly wrong
38
+ return null;
39
+ }
40
+
41
+ const isRepeated = qItem.repeats ?? false;
42
+ const pageIsMarkedAsComplete = pages[qItem.linkId].isComplete ?? false;
43
+
44
+ return (
45
+ <TabPanel
46
+ key={qItem.linkId}
47
+ sx={{ p: 0 }}
48
+ value={i.toString()}
49
+ data-test="renderer-page-panel">
50
+ <GroupItem
51
+ qItem={qItem}
52
+ qrItem={qrItem ?? null}
53
+ isRepeated={isRepeated}
54
+ groupCardElevation={1}
55
+ pageIsMarkedAsComplete={pageIsMarkedAsComplete}
56
+ pages={pages}
57
+ currentPageIndex={currentPage}
58
+ parentIsReadOnly={parentIsReadOnly}
59
+ onQrItemChange={onQrItemChange}
60
+ />
61
+ </TabPanel>
62
+ );
63
+ })}
64
+ </Grid>
65
+ </TabContext>
66
+ </Grid>
67
+ );
68
+ }
69
+
70
+ export default FormTopLevelPage;
@@ -16,7 +16,7 @@
16
16
  */
17
17
 
18
18
  import { useEffect, useState } from 'react';
19
- import type { QuestionnaireItem } from 'fhir/r4';
19
+ import type { Coding, QuestionnaireItem } from 'fhir/r4';
20
20
  import { useQuestionnaireStore } from '../stores';
21
21
 
22
22
  interface UseCodingCalculatedExpression {
@@ -56,6 +56,7 @@ function useCodingCalculatedExpression(
56
56
  calcExpression.value !== valueInString &&
57
57
  (typeof calcExpression.value === 'string' ||
58
58
  typeof calcExpression.value === 'number' ||
59
+ typeof calcExpression.value === 'object' ||
59
60
  calcExpression.value === null)
60
61
  ) {
61
62
  // update ui to show calculated value changes
@@ -70,6 +71,14 @@ function useCodingCalculatedExpression(
70
71
  return;
71
72
  }
72
73
 
74
+ // calculatedExpression value is object, check if it is a Coding object
75
+ if (typeof calcExpression.value === 'object' && objectIsCoding(calcExpression.value)) {
76
+ if (calcExpression.value.code) {
77
+ onChangeByCalcExpressionString(calcExpression.value.code);
78
+ return;
79
+ }
80
+ }
81
+
73
82
  // calculatedExpression value is a string or number
74
83
  const newValueString =
75
84
  typeof calcExpression.value === 'string'
@@ -87,4 +96,8 @@ function useCodingCalculatedExpression(
87
96
  return { calcExpUpdated: calcExpUpdated };
88
97
  }
89
98
 
99
+ function objectIsCoding(obj: any): obj is Coding {
100
+ return obj && obj.code && typeof obj.code === 'string';
101
+ }
102
+
90
103
  export default useCodingCalculatedExpression;
@@ -0,0 +1,69 @@
1
+ import { useQuestionnaireStore } from '../stores';
2
+ import type { Pages } from '../interfaces/page.interface';
3
+ import { constructPagesWithVisibility } from '../utils/page';
4
+
5
+ function useNextAndPreviousVisiblePages(
6
+ currentPageIndex?: number,
7
+ pages?: Pages
8
+ ): { previousPageIndex: number | null; nextPageIndex: number | null; numOfVisiblePages: number } {
9
+ const enableWhenIsActivated = useQuestionnaireStore.use.enableWhenIsActivated();
10
+ const enableWhenItems = useQuestionnaireStore.use.enableWhenItems();
11
+ const enableWhenExpressions = useQuestionnaireStore.use.enableWhenExpressions();
12
+
13
+ const pagesNotDefined = currentPageIndex === undefined || pages === undefined;
14
+
15
+ if (pagesNotDefined) {
16
+ return { previousPageIndex: null, nextPageIndex: null, numOfVisiblePages: 0 };
17
+ }
18
+
19
+ const pagesWithVisibility = constructPagesWithVisibility({
20
+ pages,
21
+ enableWhenIsActivated,
22
+ enableWhenItems,
23
+ enableWhenExpressions
24
+ });
25
+
26
+ return {
27
+ previousPageIndex: getPreviousPageIndex(currentPageIndex, pagesWithVisibility),
28
+ nextPageIndex: getNextPageIndex(currentPageIndex, pagesWithVisibility),
29
+ numOfVisiblePages: pagesWithVisibility.filter((tab) => tab.isVisible).length
30
+ };
31
+ }
32
+
33
+ function getPreviousPageIndex(
34
+ currentPageIndex: number,
35
+ pagesWithVisibility: { linkId: string; isVisible: boolean }[]
36
+ ): number | null {
37
+ const previousPages = pagesWithVisibility.slice(0, currentPageIndex);
38
+ const foundIndex = previousPages.reverse().findIndex((tab) => tab.isVisible);
39
+
40
+ // Previous visible tab not found
41
+ if (foundIndex === -1) {
42
+ return null;
43
+ }
44
+
45
+ // Previous visible tab less than 0
46
+ const previousPageIndex = currentPageIndex - foundIndex - 1;
47
+ if (previousPageIndex < 0) {
48
+ return null;
49
+ }
50
+
51
+ return previousPageIndex;
52
+ }
53
+
54
+ function getNextPageIndex(
55
+ currentPageIndex: number,
56
+ pagesWithVisibility: { linkId: string; isVisible: boolean }[]
57
+ ): number | null {
58
+ const subsequentPages = pagesWithVisibility.slice(currentPageIndex + 1);
59
+ const foundIndex = subsequentPages.findIndex((tab) => tab.isVisible);
60
+
61
+ // Next visible tab not found, something is wrong
62
+ if (foundIndex === -1) {
63
+ return null;
64
+ }
65
+
66
+ return currentPageIndex + foundIndex + 1;
67
+ }
68
+
69
+ export default useNextAndPreviousVisiblePages;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Page interface
3
+ *
4
+ * @property pageIndex - The index of the page
5
+ * @property isComplete - Whether the page is marked as complete
6
+ * @property isHidden - Whether the page is hidden
7
+ */
8
+ export type Page = { pageIndex: number; isComplete: boolean; isHidden: boolean };
9
+
10
+ /**
11
+ * Key-value pair of pages `Record<linkId, Page>`
12
+ */
13
+ export type Pages = Record<string, Page>;
@@ -16,6 +16,7 @@
16
16
  */
17
17
 
18
18
  import type { Tabs } from './tab.interface';
19
+ import type { Pages } from './page.interface';
19
20
  import type { Variables } from './variables.interface';
20
21
  import type { LaunchContext } from './populate.interface';
21
22
  import type { EnableWhenExpressions, EnableWhenItems } from './enableWhen.interface';
@@ -27,6 +28,7 @@ import type { InitialExpression } from './initialExpression.interface';
27
28
  export interface QuestionnaireModel {
28
29
  itemTypes: Record<string, string>;
29
30
  tabs: Tabs;
31
+ pages: Pages;
30
32
  variables: Variables;
31
33
  launchContexts: Record<string, LaunchContext>;
32
34
  enableWhenItems: EnableWhenItems;
@@ -28,6 +28,7 @@ import type { CalculatedExpression } from '../interfaces/calculatedExpression.in
28
28
  import type { EnableWhenExpressions, EnableWhenItems } from '../interfaces/enableWhen.interface';
29
29
  import type { AnswerExpression } from '../interfaces/answerExpression.interface';
30
30
  import type { Tabs } from '../interfaces/tab.interface';
31
+ import type { Pages } from '../interfaces/page.interface';
31
32
  import {
32
33
  mutateRepeatEnableWhenItemInstances,
33
34
  updateEnableWhenItemAnswer
@@ -58,6 +59,8 @@ import type { InitialExpression } from '../interfaces/initialExpression.interfac
58
59
  * @property itemTypes - Key-value pair of item types `Record<linkId, item.type>`
59
60
  * @property tabs - Key-value pair of tabs `Record<linkId, Tab>`
60
61
  * @property currentTabIndex - Index of the current tab
62
+ * @property pages - Key-value pair of pages `Record<linkId, Page>`
63
+ * @property currentPageIndex - Index of the current page
61
64
  * @property variables - Questionnaire variables object containing FHIRPath and x-fhir-query variables
62
65
  * @property launchContexts - Key-value pair of launch contexts `Record<launch context name, launch context properties>`
63
66
  * @property enableWhenItems - EnableWhenItems object containing enableWhen items and their linked questions
@@ -76,7 +79,9 @@ import type { InitialExpression } from '../interfaces/initialExpression.interfac
76
79
  * @property buildSourceQuestionnaire - Used to build the source questionnaire with the provided questionnaire and optionally questionnaire response, additional variables, terminology server url and readyOnly flag
77
80
  * @property destroySourceQuestionnaire - Used to destroy the source questionnaire and reset all properties
78
81
  * @property switchTab - Used to switch the current tab index
82
+ * @property switchPage - Used to switch the current page index
79
83
  * @property markTabAsComplete - Used to mark a tab index as complete
84
+ * @property markPageAsComplete - Used to mark a page index as complete
80
85
  * @property updateEnableWhenItem - Used to update linked enableWhen items by updating a question with a new answer
81
86
  * @property mutateRepeatEnableWhenItems - Used to add or remove instances of repeating enableWhen items
82
87
  * @property toggleEnableWhenActivation - Used to toggle enableWhen checks on/off
@@ -94,6 +99,8 @@ export interface QuestionnaireStoreType {
94
99
  itemTypes: Record<string, string>;
95
100
  tabs: Tabs;
96
101
  currentTabIndex: number;
102
+ pages: Pages;
103
+ currentPageIndex: number;
97
104
  variables: Variables;
98
105
  launchContexts: Record<string, LaunchContext>;
99
106
  enableWhenItems: EnableWhenItems;
@@ -119,7 +126,9 @@ export interface QuestionnaireStoreType {
119
126
  ) => Promise<void>;
120
127
  destroySourceQuestionnaire: () => void;
121
128
  switchTab: (newTabIndex: number) => void;
129
+ switchPage: (newPageIndex: number) => void;
122
130
  markTabAsComplete: (tabLinkId: string) => void;
131
+ markPageAsComplete: (pageLinkId: string) => void;
123
132
  updateEnableWhenItem: (
124
133
  linkId: string,
125
134
  newAnswer: QuestionnaireResponseItemAnswer[] | undefined,
@@ -155,6 +164,8 @@ export const questionnaireStore = createStore<QuestionnaireStoreType>()((set, ge
155
164
  itemTypes: {},
156
165
  tabs: {},
157
166
  currentTabIndex: 0,
167
+ pages: {},
168
+ currentPageIndex: 0,
158
169
  variables: { fhirPathVariables: {}, xFhirQueryVariables: {} },
159
170
  launchContexts: {},
160
171
  calculatedExpressions: {},
@@ -197,6 +208,7 @@ export const questionnaireStore = createStore<QuestionnaireStoreType>()((set, ge
197
208
  initialEnableWhenExpressions,
198
209
  initialCalculatedExpressions,
199
210
  firstVisibleTab,
211
+ firstVisiblePage,
200
212
  updatedFhirPathContext
201
213
  } = initialiseFormFromResponse({
202
214
  questionnaireResponse,
@@ -205,6 +217,7 @@ export const questionnaireStore = createStore<QuestionnaireStoreType>()((set, ge
205
217
  calculatedExpressions: questionnaireModel.calculatedExpressions,
206
218
  variablesFhirPath: questionnaireModel.variables.fhirPathVariables,
207
219
  tabs: questionnaireModel.tabs,
220
+ pages: questionnaireModel.pages,
208
221
  fhirPathContext: questionnaireModel.fhirPathContext
209
222
  });
210
223
 
@@ -213,6 +226,8 @@ export const questionnaireStore = createStore<QuestionnaireStoreType>()((set, ge
213
226
  itemTypes: questionnaireModel.itemTypes,
214
227
  tabs: questionnaireModel.tabs,
215
228
  currentTabIndex: firstVisibleTab,
229
+ pages: questionnaireModel.pages,
230
+ currentPageIndex: firstVisiblePage,
216
231
  variables: questionnaireModel.variables,
217
232
  launchContexts: questionnaireModel.launchContexts,
218
233
  enableWhenItems: initialEnableWhenItems,
@@ -233,6 +248,8 @@ export const questionnaireStore = createStore<QuestionnaireStoreType>()((set, ge
233
248
  itemTypes: {},
234
249
  tabs: {},
235
250
  currentTabIndex: 0,
251
+ pages: {},
252
+ currentPageIndex: 0,
236
253
  variables: { fhirPathVariables: {}, xFhirQueryVariables: {} },
237
254
  launchContexts: {},
238
255
  enableWhenItems: { singleItems: {}, repeatItems: {} },
@@ -246,6 +263,7 @@ export const questionnaireStore = createStore<QuestionnaireStoreType>()((set, ge
246
263
  fhirPathContext: {}
247
264
  }),
248
265
  switchTab: (newTabIndex: number) => set(() => ({ currentTabIndex: newTabIndex })),
266
+ switchPage: (newPageIndex: number) => set(() => ({ currentPageIndex: newPageIndex })),
249
267
  markTabAsComplete: (tabLinkId: string) => {
250
268
  const tabs = get().tabs;
251
269
  set(() => ({
@@ -255,6 +273,15 @@ export const questionnaireStore = createStore<QuestionnaireStoreType>()((set, ge
255
273
  }
256
274
  }));
257
275
  },
276
+ markPageAsComplete: (pageLinkId: string) => {
277
+ const pages = get().pages;
278
+ set(() => ({
279
+ pages: {
280
+ ...pages,
281
+ [pageLinkId]: { ...pages[pageLinkId], isComplete: !pages[pageLinkId].isComplete }
282
+ }
283
+ }));
284
+ },
258
285
  updateEnableWhenItem: (
259
286
  linkId: string,
260
287
  newAnswer: QuestionnaireResponseItemAnswer[] | undefined,
@@ -355,7 +382,8 @@ export const questionnaireStore = createStore<QuestionnaireStoreType>()((set, ge
355
382
  updatePopulatedProperties: (
356
383
  populatedResponse: QuestionnaireResponse,
357
384
  populatedContext?: Record<string, any>,
358
- persistTabIndex?: boolean
385
+ persistTabIndex?: boolean,
386
+ persistPageIndex?: boolean
359
387
  ) => {
360
388
  const initialResponseItemMap = createQuestionnaireResponseItemMap(populatedResponse);
361
389
 
@@ -379,7 +407,8 @@ export const questionnaireStore = createStore<QuestionnaireStoreType>()((set, ge
379
407
  initialEnableWhenItems,
380
408
  initialEnableWhenLinkedQuestions,
381
409
  initialEnableWhenExpressions,
382
- firstVisibleTab
410
+ firstVisibleTab,
411
+ firstVisiblePage
383
412
  } = initialiseFormFromResponse({
384
413
  questionnaireResponse: updatedResponse,
385
414
  enableWhenItems: get().enableWhenItems,
@@ -387,6 +416,7 @@ export const questionnaireStore = createStore<QuestionnaireStoreType>()((set, ge
387
416
  calculatedExpressions: initialCalculatedExpressions,
388
417
  variablesFhirPath: get().variables.fhirPathVariables,
389
418
  tabs: get().tabs,
419
+ pages: get().pages,
390
420
  fhirPathContext: updatedFhirPathContext
391
421
  });
392
422
  updatedFhirPathContext = evaluateInitialCalculatedExpressionsResult.updatedFhirPathContext;
@@ -397,6 +427,7 @@ export const questionnaireStore = createStore<QuestionnaireStoreType>()((set, ge
397
427
  enableWhenExpressions: initialEnableWhenExpressions,
398
428
  calculatedExpressions: initialCalculatedExpressions,
399
429
  currentTabIndex: persistTabIndex ? get().currentTabIndex : firstVisibleTab,
430
+ currentPageIndex: persistPageIndex ? get().currentPageIndex : firstVisiblePage,
400
431
  fhirPathContext: updatedFhirPathContext,
401
432
  populatedContext: populatedContext ?? get().populatedContext
402
433
  }));
@@ -0,0 +1,209 @@
1
+ {
2
+ "resourceType": "Questionnaire",
3
+ "id": "AuCoreAllergyIntolerance",
4
+ "extension": [
5
+ {
6
+ "url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-launchContext",
7
+ "extension": [
8
+ {
9
+ "url": "name",
10
+ "valueCoding": {
11
+ "system": "http://hl7.org/fhir/uv/sdc/CodeSystem/launchContext",
12
+ "code": "patient"
13
+ }
14
+ },
15
+ {
16
+ "url": "type",
17
+ "valueCode": "Patient"
18
+ },
19
+ {
20
+ "url": "description",
21
+ "valueString": "The patient that is to be used to pre-populate the form"
22
+ }
23
+ ]
24
+ },
25
+ {
26
+ "url": "http://hl7.org/fhir/StructureDefinition/variable",
27
+ "valueExpression": {
28
+ "name": "AllergyIntolerance",
29
+ "language": "application/x-fhir-query",
30
+ "expression": "AllergyIntolerance?patient={{%patient.id}}"
31
+ }
32
+ }
33
+ ],
34
+ "version": "0.5.0-ci-build",
35
+ "name": "AU Core AllergyIntolerance",
36
+ "title": "AU Core AllergyIntolerance",
37
+ "status": "draft",
38
+ "date": "2024-07-15",
39
+ "item": [
40
+ {
41
+ "linkId": "allergy-intolerance",
42
+ "text": "AU Core AllergyIntolerance",
43
+ "type": "group",
44
+ "repeats": false,
45
+ "item": [
46
+ {
47
+ "linkId": "patient-details",
48
+ "text": "Patient Details",
49
+ "type": "group",
50
+ "item": [
51
+ {
52
+ "extension": [
53
+ {
54
+ "url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression",
55
+ "valueExpression": {
56
+ "language": "text/fhirpath",
57
+ "expression": "%patient.id"
58
+ }
59
+ }
60
+ ],
61
+ "linkId": "patient-id",
62
+ "text": "Patient ID",
63
+ "type": "string",
64
+ "required": true,
65
+ "readOnly": true
66
+ },
67
+ {
68
+ "extension": [
69
+ {
70
+ "url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression",
71
+ "valueExpression": {
72
+ "language": "text/fhirpath",
73
+ "expression": "(%patient.name.where(use='official').select((family | (given | prefix).join(' ')).join(', ').where($this != '') | text)).first()"
74
+ }
75
+ }
76
+ ],
77
+ "linkId": "patient-name",
78
+ "text": "Patient Name",
79
+ "type": "string",
80
+ "required": true,
81
+ "readOnly": true
82
+ }
83
+ ]
84
+ },
85
+ {
86
+ "linkId": "first-resource-note",
87
+ "text": "This questionnaire only uses the first AllergyTolerance resource of a reference patient.",
88
+ "_text": {
89
+ "extension": [
90
+ {
91
+ "url": "http://hl7.org/fhir/StructureDefinition/rendering-xhtml",
92
+ "valueString": "<div xmlns=\"http://www.w3.org/1999/xhtml\" style=\"padding-bottom: 8px;\">\r\n <b>This questionnaire only uses the first AllergyIntolerance resource of a reference patient.</b>\r\n</div>"
93
+ }
94
+ ]
95
+ },
96
+ "type": "display"
97
+ },
98
+ {
99
+ "extension": [
100
+ {
101
+ "url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression",
102
+ "valueExpression": {
103
+ "language": "text/fhirpath",
104
+ "expression": "%AllergyIntolerance.entry[0].resource.clinicalStatus.coding"
105
+ }
106
+ }
107
+ ],
108
+ "linkId": "clinical-status",
109
+ "text": "Clinical Status",
110
+ "type": "choice",
111
+ "answerValueSet": "http://hl7.org/fhir/ValueSet/allergyintolerance-clinical"
112
+ },
113
+ {
114
+ "extension": [
115
+ {
116
+ "url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression",
117
+ "valueExpression": {
118
+ "language": "text/fhirpath",
119
+ "expression": "%AllergyIntolerance.entry[0].resource.verificationStatus.coding"
120
+ }
121
+ }
122
+ ],
123
+ "linkId": "verification-status",
124
+ "text": "Verification Status",
125
+ "type": "choice",
126
+ "answerValueSet": "http://hl7.org/fhir/ValueSet/allergyintolerance-verification"
127
+ },
128
+ {
129
+ "extension": [
130
+ {
131
+ "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl",
132
+ "valueCodeableConcept": {
133
+ "coding": [
134
+ {
135
+ "system": "http://hl7.org/fhir/questionnaire-item-control",
136
+ "code": "autocomplete"
137
+ }
138
+ ]
139
+ }
140
+ },
141
+ {
142
+ "url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression",
143
+ "valueExpression": {
144
+ "language": "text/fhirpath",
145
+ "expression": "%AllergyIntolerance.entry[0].resource.code.coding"
146
+ }
147
+ }
148
+ ],
149
+ "linkId": "code",
150
+ "text": "Code",
151
+ "type": "open-choice",
152
+ "required": true,
153
+ "answerValueSet": "https://healthterminologies.gov.au/fhir/ValueSet/indicator-hypersensitivity-intolerance-to-substance-2"
154
+ },
155
+ {
156
+ "extension": [
157
+ {
158
+ "url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression",
159
+ "valueExpression": {
160
+ "language": "text/fhirpath",
161
+ "expression": "%AllergyIntolerance.entry[0].resource.onset"
162
+ }
163
+ }
164
+ ],
165
+ "linkId": "onset",
166
+ "text": "Onset[x]",
167
+ "type": "string"
168
+ },
169
+ {
170
+ "linkId": "reaction",
171
+ "text": "Reaction",
172
+ "type": "group",
173
+ "item": [
174
+ {
175
+ "extension": [
176
+ {
177
+ "url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression",
178
+ "valueExpression": {
179
+ "language": "text/fhirpath",
180
+ "expression": "%AllergyIntolerance.entry[0].resource.reaction.manifestation[0].coding"
181
+ }
182
+ }
183
+ ],
184
+ "linkId": "reaction-manifestation",
185
+ "text": "Reaction Manifestation",
186
+ "type": "choice",
187
+ "answerValueSet": "https://healthterminologies.gov.au/fhir/ValueSet/clinical-finding-1"
188
+ },
189
+ {
190
+ "extension": [
191
+ {
192
+ "url": "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression",
193
+ "valueExpression": {
194
+ "language": "text/fhirpath",
195
+ "expression": "%AllergyIntolerance.entry[0].resource.reaction.severity"
196
+ }
197
+ }
198
+ ],
199
+ "linkId": "reaction-severity",
200
+ "text": "Reaction Severity",
201
+ "type": "choice",
202
+ "answerValueSet": "http://hl7.org/fhir/ValueSet/reaction-event-severity"
203
+ }
204
+ ]
205
+ }
206
+ ]
207
+ }
208
+ ]
209
+ }