@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
@@ -17,6 +17,7 @@
17
17
 
18
18
  import { evaluateInitialEnableWhenExpressions } from './enableWhenExpression';
19
19
  import { getFirstVisibleTab } from './tabs';
20
+ import { getFirstVisiblePage } from './page';
20
21
  import type {
21
22
  Expression,
22
23
  Questionnaire,
@@ -28,6 +29,7 @@ import type {
28
29
  } from 'fhir/r4';
29
30
  import type { EnableWhenExpressions, EnableWhenItems } from '../interfaces/enableWhen.interface';
30
31
  import type { Tabs } from '../interfaces/tab.interface';
32
+ import type { Pages } from '../interfaces/page.interface';
31
33
  import { assignPopulatedAnswersToEnableWhen } from './enableWhen';
32
34
  import type { CalculatedExpression } from '../interfaces/calculatedExpression.interface';
33
35
  import { evaluateInitialCalculatedExpressions } from './calculatedExpression';
@@ -320,6 +322,7 @@ export interface initialFormFromResponseParams {
320
322
  calculatedExpressions: Record<string, CalculatedExpression[]>;
321
323
  variablesFhirPath: Record<string, Expression[]>;
322
324
  tabs: Tabs;
325
+ pages: Pages;
323
326
  fhirPathContext: Record<string, any>;
324
327
  }
325
328
 
@@ -329,6 +332,7 @@ export function initialiseFormFromResponse(params: initialFormFromResponseParams
329
332
  initialEnableWhenExpressions: EnableWhenExpressions;
330
333
  initialCalculatedExpressions: Record<string, CalculatedExpression[]>;
331
334
  firstVisibleTab: number;
335
+ firstVisiblePage: number;
332
336
  updatedFhirPathContext: Record<string, any>;
333
337
  } {
334
338
  const {
@@ -338,6 +342,7 @@ export function initialiseFormFromResponse(params: initialFormFromResponseParams
338
342
  calculatedExpressions,
339
343
  variablesFhirPath,
340
344
  tabs,
345
+ pages,
341
346
  fhirPathContext
342
347
  } = params;
343
348
  const initialResponseItemMap = createQuestionnaireResponseItemMap(questionnaireResponse);
@@ -373,12 +378,18 @@ export function initialiseFormFromResponse(params: initialFormFromResponseParams
373
378
  ? getFirstVisibleTab(tabs, initialisedItems, initialEnableWhenExpressions)
374
379
  : 0;
375
380
 
381
+ const firstVisiblePage =
382
+ Object.keys(pages).length > 0
383
+ ? getFirstVisiblePage(pages, initialisedItems, initialEnableWhenExpressions)
384
+ : 0;
385
+
376
386
  return {
377
387
  initialEnableWhenItems: initialisedItems,
378
388
  initialEnableWhenLinkedQuestions: linkedQuestions,
379
389
  initialEnableWhenExpressions,
380
390
  initialCalculatedExpressions,
381
391
  firstVisibleTab,
392
+ firstVisiblePage,
382
393
  updatedFhirPathContext
383
394
  };
384
395
  }
@@ -0,0 +1,134 @@
1
+ import type { Pages } from '../interfaces/page.interface';
2
+ import type { EnableWhenExpressions, EnableWhenItems } from '../interfaces/enableWhen.interface';
3
+ import type { QuestionnaireItem } from 'fhir/r4';
4
+ import { isSpecificItemControl } from './itemControl';
5
+ import { isHiddenByEnableWhen } from './qItem';
6
+ import { structuredDataCapture } from 'fhir-sdc-helpers';
7
+
8
+ export function getFirstVisiblePage(
9
+ pages: Pages,
10
+ enableWhenItems: EnableWhenItems,
11
+ enableWhenExpressions: EnableWhenExpressions
12
+ ) {
13
+ // Only singleEnableWhenItems are relevant for page operations
14
+ const { singleItems } = enableWhenItems;
15
+ const { singleExpressions } = enableWhenExpressions;
16
+
17
+ return Object.entries(pages)
18
+ .sort(([, pageA], [, pageB]) => pageA.pageIndex - pageB.pageIndex)
19
+ .findIndex(([pageLinkId, page]) => {
20
+ if (page.isHidden) {
21
+ return false;
22
+ }
23
+
24
+ const singleItem = singleItems[pageLinkId];
25
+ if (singleItem) {
26
+ return singleItem.isEnabled;
27
+ }
28
+
29
+ const singleExpression = singleExpressions[pageLinkId];
30
+ if (singleExpression) {
31
+ return singleExpression.isEnabled;
32
+ }
33
+
34
+ return true;
35
+ });
36
+ }
37
+
38
+ /**
39
+ * Checks if all of the items in a qItem array is a page item
40
+ * Returns true if all items is page item
41
+ * Returns false if only have one item
42
+ *
43
+ * @author Riza Nafis
44
+ */
45
+ export function everyIsPages(topLevelQItem: QuestionnaireItem[] | undefined): boolean {
46
+ if (!topLevelQItem) return false;
47
+
48
+ if (isPageContainer(topLevelQItem)) return false;
49
+
50
+ return topLevelQItem.every((i: QuestionnaireItem) => isPage(i));
51
+ }
52
+
53
+ export function isPageContainer(topLevelQItem: QuestionnaireItem[] | undefined): boolean {
54
+ const anyPage = topLevelQItem?.filter(isPage);
55
+
56
+ if (!anyPage) return false;
57
+
58
+ return anyPage.some((page) => page.item?.every((i) => i.type === 'group') || false);
59
+ }
60
+
61
+ /**
62
+ * Checks if any of the items in a qItem array is a page item
63
+ * Returns true if there is at least one page item
64
+ *
65
+ * @author Riza Nafis
66
+ */
67
+ export function containsPages(topLevelQItem: QuestionnaireItem): boolean {
68
+ if (!topLevelQItem.item) {
69
+ return false;
70
+ }
71
+
72
+ const pages = topLevelQItem.item.filter((i) => isPage(i));
73
+ return pages.length > 0;
74
+ }
75
+
76
+ /**
77
+ * Check if a qItem is a page item
78
+ *
79
+ * @author Riza Nafis
80
+ */
81
+ export function isPage(item: QuestionnaireItem) {
82
+ return isSpecificItemControl(item, 'page');
83
+ }
84
+
85
+ /**
86
+ * Create a `Record<linkId, Pages>` key-value pair for all page items in a qItem array
87
+ *
88
+ * @author Riza Nafis
89
+ */
90
+ export function constructPagesWithProperties(
91
+ qItems: QuestionnaireItem[] | undefined,
92
+ hasPageContainer: boolean
93
+ ): Pages {
94
+ if (!qItems) return {};
95
+
96
+ const qItemPages = hasPageContainer ? qItems : qItems.filter(isPage);
97
+
98
+ const pages: Pages = {};
99
+ for (const [i, qItem] of qItemPages.entries()) {
100
+ pages[qItem.linkId] = {
101
+ pageIndex: i,
102
+ isComplete: false,
103
+ isHidden: structuredDataCapture.getHidden(qItem) ?? false
104
+ };
105
+ }
106
+ return pages;
107
+ }
108
+
109
+ interface contructPagesWithVisibilityParams {
110
+ pages: Pages;
111
+ enableWhenIsActivated: boolean;
112
+ enableWhenItems: EnableWhenItems;
113
+ enableWhenExpressions: EnableWhenExpressions;
114
+ }
115
+
116
+ export function constructPagesWithVisibility(
117
+ params: contructPagesWithVisibilityParams
118
+ ): { linkId: string; isVisible: boolean }[] {
119
+ const { pages, enableWhenIsActivated, enableWhenItems, enableWhenExpressions } = params;
120
+
121
+ return Object.entries(pages).map(([linkId]) => {
122
+ const isVisible = !isHiddenByEnableWhen({
123
+ linkId,
124
+ enableWhenIsActivated,
125
+ enableWhenItems,
126
+ enableWhenExpressions
127
+ });
128
+
129
+ return {
130
+ linkId,
131
+ isVisible
132
+ };
133
+ });
134
+ }
@@ -17,11 +17,13 @@
17
17
 
18
18
  import type { Questionnaire } from 'fhir/r4';
19
19
  import type { Tabs } from '../../interfaces/tab.interface';
20
+ import type { Pages } from '../../interfaces/page.interface';
20
21
  import type { LaunchContext } from '../../interfaces/populate.interface';
21
22
  import type { QuestionnaireModel } from '../../interfaces/questionnaireStore.interface';
22
23
  import { extractLaunchContexts } from './extractLaunchContext';
23
24
  import { extractQuestionnaireLevelVariables } from './extractVariables';
24
25
  import { extractTabs } from './extractTabs';
26
+ import { extractPages } from './extractPages';
25
27
  import { extractContainedValueSets } from './extractContainedValueSets';
26
28
  import { extractOtherExtensions } from './extractOtherExtensions';
27
29
  import type { Variables } from '../../interfaces/variables.interface';
@@ -41,6 +43,7 @@ export async function createQuestionnaireModel(
41
43
 
42
44
  const itemTypes: Record<string, string> = Object.fromEntries(getLinkIdTypeTuples(questionnaire));
43
45
  const tabs: Tabs = extractTabs(questionnaire);
46
+ const pages: Pages = extractPages(questionnaire);
44
47
 
45
48
  const launchContexts: Record<string, LaunchContext> = extractLaunchContexts(questionnaire);
46
49
 
@@ -98,6 +101,7 @@ export async function createQuestionnaireModel(
98
101
  return {
99
102
  itemTypes,
100
103
  tabs,
104
+ pages,
101
105
  variables,
102
106
  launchContexts,
103
107
  enableWhenItems,
@@ -116,6 +120,7 @@ function createEmptyModel(): QuestionnaireModel {
116
120
  return {
117
121
  itemTypes: {},
118
122
  tabs: {},
123
+ pages: {},
119
124
  variables: { fhirPathVariables: {}, xFhirQueryVariables: {} },
120
125
  launchContexts: {},
121
126
  calculatedExpressions: {},
@@ -0,0 +1,24 @@
1
+ import type { Questionnaire } from 'fhir/r4';
2
+ import type { Pages } from '../../interfaces/page.interface';
3
+ import { constructPagesWithProperties, isPage, isPageContainer } from '../page';
4
+
5
+ export function extractPages(questionnaire: Questionnaire): Pages {
6
+ if (!questionnaire.item || questionnaire.item.length === 0) {
7
+ return {};
8
+ }
9
+
10
+ if (!isPageContainer(questionnaire.item)) {
11
+ return constructPagesWithProperties(questionnaire.item, false);
12
+ }
13
+
14
+ let totalPages = {};
15
+ for (const topLevelItem of questionnaire.item) {
16
+ const items = topLevelItem.item;
17
+ const topLevelItemIsPageContainer = isPage(topLevelItem);
18
+
19
+ const pages = constructPagesWithProperties(items, topLevelItemIsPageContainer);
20
+ totalPages = { ...totalPages, ...pages };
21
+ }
22
+
23
+ return totalPages;
24
+ }