@aehrc/smart-forms-renderer 0.40.0 → 0.40.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 (108) hide show
  1. package/lib/components/FormComponents/ItemParts/ItemFieldGrid.js +6 -2
  2. package/lib/components/FormComponents/ItemParts/ItemFieldGrid.js.map +1 -1
  3. package/lib/components/FormComponents/RepeatGroup/RepeatGroup.js +3 -3
  4. package/lib/components/FormComponents/RepeatGroup/RepeatGroup.js.map +1 -1
  5. package/lib/components/FormComponents/RepeatGroup/RepeatGroupView.js +6 -6
  6. package/lib/components/FormComponents/RepeatGroup/RepeatGroupView.js.map +1 -1
  7. package/lib/components/FormComponents/RepeatItem/RepeatItem.js +5 -5
  8. package/lib/components/FormComponents/RepeatItem/RepeatItem.js.map +1 -1
  9. package/lib/components/FormComponents/Tables/GroupTable.js +10 -10
  10. package/lib/components/FormComponents/Tables/GroupTable.js.map +1 -1
  11. package/lib/components/FormComponents/Tables/GroupTableBody.d.ts +1 -1
  12. package/lib/components/FormComponents/Tables/GroupTableBody.js +3 -3
  13. package/lib/components/FormComponents/Tables/GroupTableBody.js.map +1 -1
  14. package/lib/components/FormComponents/Tables/GroupTableRow.d.ts +1 -1
  15. package/lib/components/FormComponents/Tables/GroupTableRow.js +4 -4
  16. package/lib/components/FormComponents/Tables/GroupTableRow.js.map +1 -1
  17. package/lib/components/FormComponents/Tables/GroupTableView.d.ts +1 -1
  18. package/lib/components/Renderer/FormTopLevelItem.js +13 -1
  19. package/lib/components/Renderer/FormTopLevelItem.js.map +1 -1
  20. package/lib/hooks/useDateValidation.js +2 -2
  21. package/lib/hooks/useDateValidation.js.map +1 -1
  22. package/lib/hooks/useGroupTableRows.d.ts +1 -1
  23. package/lib/hooks/useGroupTableRows.js +3 -3
  24. package/lib/hooks/useGroupTableRows.js.map +1 -1
  25. package/lib/hooks/useInitialiseGroupTable.d.ts +1 -1
  26. package/lib/hooks/useInitialiseGroupTable.js +10 -16
  27. package/lib/hooks/useInitialiseGroupTable.js.map +1 -1
  28. package/lib/hooks/useInitialiseRepeatAnswers.d.ts +1 -1
  29. package/lib/hooks/useInitialiseRepeatAnswers.js +9 -12
  30. package/lib/hooks/useInitialiseRepeatAnswers.js.map +1 -1
  31. package/lib/hooks/useInitialiseRepeatGroups.d.ts +2 -2
  32. package/lib/hooks/useInitialiseRepeatGroups.js +11 -20
  33. package/lib/hooks/useInitialiseRepeatGroups.js.map +1 -1
  34. package/lib/hooks/useRepeatGroups.js +6 -2
  35. package/lib/hooks/useRepeatGroups.js.map +1 -1
  36. package/lib/index.d.ts +1 -1
  37. package/lib/index.js +1 -1
  38. package/lib/index.js.map +1 -1
  39. package/lib/interfaces/groupTable.interface.d.ts +1 -1
  40. package/lib/interfaces/repeatGroup.interface.d.ts +1 -1
  41. package/lib/theme/palette.js +0 -4
  42. package/lib/theme/palette.js.map +1 -1
  43. package/lib/utils/groupTable.js +1 -1
  44. package/lib/utils/groupTable.js.map +1 -1
  45. package/lib/utils/index.d.ts +1 -1
  46. package/lib/utils/index.js +1 -1
  47. package/lib/utils/index.js.map +1 -1
  48. package/lib/utils/manageForm.d.ts +7 -0
  49. package/lib/utils/manageForm.js +15 -1
  50. package/lib/utils/manageForm.js.map +1 -1
  51. package/lib/utils/repeatId.d.ts +4 -0
  52. package/lib/utils/repeatId.js +93 -0
  53. package/lib/utils/repeatId.js.map +1 -0
  54. package/package.json +1 -1
  55. package/src/components/FormComponents/ItemParts/ItemFieldGrid.tsx +8 -2
  56. package/src/components/FormComponents/RepeatGroup/RepeatGroup.tsx +3 -3
  57. package/src/components/FormComponents/RepeatGroup/RepeatGroupView.tsx +6 -6
  58. package/src/components/FormComponents/RepeatItem/RepeatItem.tsx +7 -5
  59. package/src/components/FormComponents/Tables/GroupTable.tsx +13 -10
  60. package/src/components/FormComponents/Tables/GroupTableBody.tsx +5 -5
  61. package/src/components/FormComponents/Tables/GroupTableRow.tsx +5 -5
  62. package/src/components/FormComponents/Tables/GroupTableView.tsx +1 -1
  63. package/src/components/Renderer/FormTopLevelItem.tsx +33 -1
  64. package/src/hooks/useDateValidation.tsx +2 -2
  65. package/src/hooks/useGroupTableRows.ts +3 -3
  66. package/src/hooks/useInitialiseGroupTable.ts +13 -17
  67. package/src/hooks/useInitialiseRepeatAnswers.ts +9 -12
  68. package/src/hooks/useInitialiseRepeatGroups.ts +14 -25
  69. package/src/hooks/useRepeatGroups.ts +8 -4
  70. package/src/index.ts +1 -0
  71. package/src/interfaces/groupTable.interface.ts +1 -1
  72. package/src/interfaces/repeatGroup.interface.ts +1 -1
  73. package/src/stories/assets/questionnaires/QIdRemoverDebugger.ts +161 -0
  74. package/src/stories/storybookWrappers/BuildFormButtonForStorybook.tsx +1 -1
  75. package/src/stories/storybookWrappers/IdRemoverButtonForStorybook.tsx +51 -0
  76. package/src/stories/storybookWrappers/IdRemoverDebuggerWrapperForStorybook.tsx +84 -0
  77. package/src/stories/testing/IdRemoverDebuggerWrapper.stories.tsx +39 -0
  78. package/src/tests/extractObservation.test.ts +2 -5
  79. package/src/theme/palette.ts +0 -4
  80. package/src/utils/groupTable.ts +1 -1
  81. package/src/utils/index.ts +7 -1
  82. package/src/utils/manageForm.ts +28 -1
  83. package/src/utils/repeatId.ts +123 -0
  84. package/CHANGELOG.md +0 -43
  85. package/lib/hooks/useRepeatAnswers.d.ts +0 -4
  86. package/lib/hooks/useRepeatAnswers.js +0 -34
  87. package/lib/hooks/useRepeatAnswers.js.map +0 -1
  88. package/lib/interfaces/repeatItem.interface.d.ts +0 -5
  89. package/lib/interfaces/repeatItem.interface.js +0 -2
  90. package/lib/interfaces/repeatItem.interface.js.map +0 -1
  91. package/lib/utils/answerExpression.d.ts +0 -18
  92. package/lib/utils/answerExpression.js +0 -133
  93. package/lib/utils/answerExpression.js.map +0 -1
  94. package/lib/utils/dynamicValueSet.d.ts +0 -5
  95. package/lib/utils/dynamicValueSet.js +0 -96
  96. package/lib/utils/dynamicValueSet.js.map +0 -1
  97. package/lib/utils/fhirpathAsyncUtils/fhirpath-async.d.ts +0 -14
  98. package/lib/utils/fhirpathAsyncUtils/fhirpath-async.js +0 -639
  99. package/lib/utils/fhirpathAsyncUtils/fhirpath-async.js.map +0 -1
  100. package/lib/utils/fhirpathAsyncUtils/outcome-utils.d.ts +0 -3
  101. package/lib/utils/fhirpathAsyncUtils/outcome-utils.js +0 -41
  102. package/lib/utils/fhirpathAsyncUtils/outcome-utils.js.map +0 -1
  103. package/lib/utils/questionnaireStoreUtils/extractPreferredTerminologyServerUrls.d.ts +0 -3
  104. package/lib/utils/questionnaireStoreUtils/extractPreferredTerminologyServerUrls.js +0 -34
  105. package/lib/utils/questionnaireStoreUtils/extractPreferredTerminologyServerUrls.js.map +0 -1
  106. package/lib/utils/updateQr.d.ts +0 -9
  107. package/lib/utils/updateQr.js +0 -55
  108. package/lib/utils/updateQr.js.map +0 -1
@@ -25,13 +25,13 @@ import type {
25
25
  PropsWithQrRepeatGroupChangeHandler,
26
26
  PropsWithShowMinimalViewAttribute
27
27
  } from '../../../interfaces/renderProps.interface';
28
- import { nanoid } from 'nanoid';
29
28
  import useReadOnly from '../../../hooks/useReadOnly';
30
29
  import GroupTableView from './GroupTableView';
31
30
  import type { GroupTableRowModel } from '../../../interfaces/groupTable.interface';
32
31
  import { getGroupTableItemsToUpdate } from '../../../utils/groupTable';
33
32
  import useGroupTableRows from '../../../hooks/useGroupTableRows';
34
33
  import { flushSync } from 'react-dom';
34
+ import { generateNewRepeatId } from '../../../utils/repeatId';
35
35
 
36
36
  interface GroupTableProps
37
37
  extends PropsWithQrRepeatGroupChangeHandler,
@@ -62,7 +62,10 @@ function GroupTable(props: GroupTableProps) {
62
62
 
63
63
  const readOnly = useReadOnly(qItem, parentIsReadOnly);
64
64
 
65
- const { tableRows, selectedIds, setTableRows, setSelectedIds } = useGroupTableRows(qrItems);
65
+ const { tableRows, selectedIds, setTableRows, setSelectedIds } = useGroupTableRows(
66
+ qItem.linkId,
67
+ qrItems
68
+ );
66
69
 
67
70
  // Generate item labels as table headers
68
71
  const qItems = qItem.item;
@@ -101,7 +104,7 @@ function GroupTable(props: GroupTableProps) {
101
104
  const updatedTableRows = [...tableRows];
102
105
 
103
106
  const rowToRemove = updatedTableRows[index];
104
- const updatedSelectedIds = selectedIds.filter((id) => id !== rowToRemove.nanoId);
107
+ const updatedSelectedIds = selectedIds.filter((id) => id !== rowToRemove.id);
105
108
 
106
109
  updatedTableRows.splice(index, 1);
107
110
 
@@ -114,21 +117,21 @@ function GroupTable(props: GroupTableProps) {
114
117
  }
115
118
 
116
119
  function handleAddRow() {
117
- const newRowNanoId = nanoid();
120
+ const newRowId = generateNewRepeatId(qItem.linkId);
118
121
  setTableRows([
119
122
  ...tableRows,
120
123
  {
121
- nanoId: newRowNanoId,
124
+ id: newRowId,
122
125
  qrItem: null
123
126
  }
124
127
  ]);
125
- setSelectedIds([...selectedIds, newRowNanoId]);
128
+ setSelectedIds([...selectedIds, newRowId]);
126
129
  }
127
130
 
128
131
  function handleSelectAll() {
129
132
  // deselect all if all are selected, otherwise select all
130
133
  const updatedTableIds =
131
- selectedIds.length === tableRows.length ? [] : tableRows.map((tableRow) => tableRow.nanoId);
134
+ selectedIds.length === tableRows.length ? [] : tableRows.map((tableRow) => tableRow.id);
132
135
  setSelectedIds(updatedTableIds);
133
136
  onQrRepeatGroupChange({
134
137
  linkId: qItem.linkId,
@@ -136,12 +139,12 @@ function GroupTable(props: GroupTableProps) {
136
139
  });
137
140
  }
138
141
 
139
- function handleSelectRow(nanoId: string) {
142
+ function handleSelectRow(rowId: string) {
140
143
  const updatedSelectedIds = [...selectedIds];
141
144
 
142
- const index = updatedSelectedIds.indexOf(nanoId);
145
+ const index = updatedSelectedIds.indexOf(rowId);
143
146
  if (index === -1) {
144
- updatedSelectedIds.push(nanoId);
147
+ updatedSelectedIds.push(rowId);
145
148
  } else {
146
149
  updatedSelectedIds.splice(index, 1);
147
150
  }
@@ -41,7 +41,7 @@ interface GroupTableBodyProps
41
41
  qItemsIndexMap: Record<string, number>;
42
42
  onRowChange: (newQrRow: QuestionnaireResponseItem, index: number) => void;
43
43
  onRemoveRow: (index: number) => void;
44
- onSelectRow: (nanoId: string) => void;
44
+ onSelectRow: (rowId: string) => void;
45
45
  onReorderRows: (newTableRows: GroupTableRowModel[]) => void;
46
46
  }
47
47
 
@@ -80,8 +80,8 @@ function GroupTableBody(props: GroupTableBodyProps) {
80
80
  <Droppable droppableId="gtable_rows" direction="vertical">
81
81
  {(droppableProvided, snapshot) => (
82
82
  <TableBody ref={droppableProvided.innerRef} {...droppableProvided.droppableProps}>
83
- {tableRows.map(({ nanoId, qrItem: nullableQrItem }, index) => {
84
- const itemIsSelected = selectedIds.indexOf(nanoId) !== -1;
83
+ {tableRows.map(({ id, qrItem: nullableQrItem }, index) => {
84
+ const itemIsSelected = selectedIds.indexOf(id) !== -1;
85
85
  const answeredQrItem = createEmptyQrItem(tableQItem, undefined);
86
86
  if (nullableQrItem) {
87
87
  answeredQrItem.item = nullableQrItem.item;
@@ -89,9 +89,9 @@ function GroupTableBody(props: GroupTableBodyProps) {
89
89
 
90
90
  return (
91
91
  <GroupTableRow
92
- key={nanoId}
92
+ key={id}
93
93
  index={index}
94
- nanoId={nanoId}
94
+ rowId={id}
95
95
  tableQItem={tableQItem}
96
96
  answeredQrItem={answeredQrItem}
97
97
  nullableQrItem={nullableQrItem}
@@ -39,7 +39,7 @@ interface GroupTableRowProps
39
39
  PropsWithShowMinimalViewAttribute,
40
40
  PropsWithParentIsReadOnlyAttribute,
41
41
  TableRowProps {
42
- nanoId: string;
42
+ rowId: string;
43
43
  index: number;
44
44
  tableQItem: QuestionnaireItem;
45
45
  answeredQrItem: QuestionnaireResponseItem;
@@ -58,7 +58,7 @@ interface GroupTableRowProps
58
58
 
59
59
  function GroupTableRow(props: GroupTableRowProps) {
60
60
  const {
61
- nanoId,
61
+ rowId,
62
62
  index,
63
63
  tableQItem,
64
64
  answeredQrItem,
@@ -78,7 +78,7 @@ function GroupTableRow(props: GroupTableRowProps) {
78
78
 
79
79
  if (isRepeated) {
80
80
  return (
81
- <Draggable draggableId={nanoId} index={index}>
81
+ <Draggable draggableId={rowId} index={index}>
82
82
  {(draggableProvided, snapshot) => (
83
83
  <StyledGroupTableRow
84
84
  itemIsDragged={snapshot.isDragging}
@@ -106,7 +106,7 @@ function GroupTableRow(props: GroupTableRowProps) {
106
106
  <SelectRowButton
107
107
  isSelected={itemIsSelected}
108
108
  readOnly={readOnly}
109
- onSelectItem={() => onSelectRow(nanoId)}
109
+ onSelectItem={() => onSelectRow(rowId)}
110
110
  />
111
111
  </>
112
112
  )}
@@ -150,7 +150,7 @@ function GroupTableRow(props: GroupTableRowProps) {
150
150
  <SelectRowButton
151
151
  isSelected={itemIsSelected}
152
152
  readOnly={readOnly}
153
- onSelectItem={() => onSelectRow(nanoId)}
153
+ onSelectItem={() => onSelectRow(rowId)}
154
154
  />
155
155
  </>
156
156
  )}
@@ -54,7 +54,7 @@ interface GroupTableViewProps
54
54
  onAddRow: () => void;
55
55
  onRowChange: (newQrRow: QuestionnaireResponseItem, index: number) => void;
56
56
  onRemoveRow: (index: number) => void;
57
- onSelectRow: (nanoId: string) => void;
57
+ onSelectRow: (rowId: string) => void;
58
58
  onSelectAll: () => void;
59
59
  onReorderRows: (newTableRows: GroupTableRowModel[]) => void;
60
60
  }
@@ -34,8 +34,10 @@ import useHidden from '../../hooks/useHidden';
34
34
  import GroupItemSwitcher from '../FormComponents/GroupItem/GroupItemSwitcher';
35
35
  import useReadOnly from '../../hooks/useReadOnly';
36
36
  import Box from '@mui/material/Box';
37
- import { isSpecificItemControl } from '../../utils';
37
+ import { isRepeatItemAndNotCheckbox, isSpecificItemControl } from '../../utils';
38
38
  import GroupTable from '../FormComponents/Tables/GroupTable';
39
+ import RepeatItem from '../FormComponents/RepeatItem/RepeatItem';
40
+ import GridGroup from '../FormComponents/GridGroup/GridGroup';
39
41
 
40
42
  interface FormTopLevelItemProps
41
43
  extends PropsWithQrItemChangeHandler,
@@ -127,6 +129,20 @@ function FormTopLevelItem(props: FormTopLevelItemProps) {
127
129
 
128
130
  // If form is untabbed, it is rendered as a regular group
129
131
  if (itemIsGroup) {
132
+ // Item is 'grid'
133
+ const itemIsGrid = isSpecificItemControl(topLevelQItem, 'grid');
134
+ if (itemIsGrid) {
135
+ return (
136
+ <GridGroup
137
+ qItem={topLevelQItem}
138
+ qrItem={topLevelQRItem}
139
+ groupCardElevation={1}
140
+ parentIsReadOnly={parentIsReadOnly}
141
+ onQrItemChange={onQrItemChange}
142
+ />
143
+ );
144
+ }
145
+
130
146
  // GroupTable "gtable" can be rendered with either repeats:true or false
131
147
  if (isSpecificItemControl(topLevelQItem, 'gtable')) {
132
148
  return (
@@ -156,6 +172,22 @@ function FormTopLevelItem(props: FormTopLevelItemProps) {
156
172
  }
157
173
 
158
174
  // Otherwise, it is rendered as a non-group item
175
+ const itemRepeatsAndIsNotCheckbox = isRepeatItemAndNotCheckbox(topLevelQItem);
176
+ if (itemRepeatsAndIsNotCheckbox) {
177
+ return (
178
+ <Box mt={1}>
179
+ <RepeatItem
180
+ key={topLevelQItem.linkId}
181
+ qItem={topLevelQItem}
182
+ qrItem={topLevelQRItem}
183
+ groupCardElevation={1}
184
+ parentIsReadOnly={readOnly}
185
+ onQrItemChange={onQrItemChange}
186
+ />
187
+ </Box>
188
+ );
189
+ }
190
+
159
191
  return (
160
192
  <Box mt={1}>
161
193
  <SingleItem
@@ -55,8 +55,8 @@ function useDateValidation(input: string, parseFail: boolean = false): string |
55
55
 
56
56
  const matches = input.split('/');
57
57
 
58
- if (validateTwoMatches(matches[0], matches[1])) {
59
- return null;
58
+ if (!validateTwoMatches(matches[0], matches[1])) {
59
+ return 'Input is an invalid date.';
60
60
  }
61
61
 
62
62
  return null;
@@ -19,12 +19,12 @@ import { useState } from 'react';
19
19
  import useInitialiseGroupTable from './useInitialiseGroupTable';
20
20
  import type { QuestionnaireResponseItem } from 'fhir/r4';
21
21
 
22
- function useGroupTableRows(qrItems: QuestionnaireResponseItem[]) {
23
- const initialisedGroupTableRows = useInitialiseGroupTable(qrItems);
22
+ function useGroupTableRows(linkId: string, qrItems: QuestionnaireResponseItem[]) {
23
+ const initialisedGroupTableRows = useInitialiseGroupTable(linkId, qrItems);
24
24
 
25
25
  const [tableRows, setTableRows] = useState(initialisedGroupTableRows);
26
26
  const [selectedIds, setSelectedIds] = useState<string[]>(
27
- initialisedGroupTableRows.map((row) => row.nanoId)
27
+ initialisedGroupTableRows.map((row) => row.id)
28
28
  );
29
29
 
30
30
  return { tableRows, selectedIds, setTableRows, setSelectedIds };
@@ -16,27 +16,23 @@
16
16
  */
17
17
 
18
18
  import type { QuestionnaireResponseItem } from 'fhir/r4';
19
- import { nanoid } from 'nanoid';
20
19
  import type { GroupTableRowModel } from '../interfaces/groupTable.interface';
20
+ import { generateExistingRepeatId, generateNewRepeatId } from '../utils/repeatId';
21
21
 
22
- function useInitialiseGroupTable(qrItems: QuestionnaireResponseItem[]): GroupTableRowModel[] {
23
- let initialGroupTableRows: GroupTableRowModel[] = [
24
- {
25
- nanoId: nanoid(),
26
- qrItem: null
27
- }
28
- ];
29
-
30
- if (qrItems.length > 0) {
31
- initialGroupTableRows = qrItems.map((qrItem) => {
32
- return {
33
- nanoId: nanoid(),
34
- qrItem
35
- };
36
- });
22
+ function useInitialiseGroupTable(
23
+ linkId: string,
24
+ qrItems: QuestionnaireResponseItem[]
25
+ ): GroupTableRowModel[] {
26
+ if (qrItems.length === 0) {
27
+ return [{ id: generateNewRepeatId(linkId), qrItem: null }];
37
28
  }
38
29
 
39
- return initialGroupTableRows;
30
+ return qrItems.map((qrItem, index) => {
31
+ return {
32
+ id: generateExistingRepeatId(linkId, index),
33
+ qrItem
34
+ };
35
+ });
40
36
  }
41
37
 
42
38
  export default useInitialiseGroupTable;
@@ -16,26 +16,23 @@
16
16
  */
17
17
 
18
18
  import type { QuestionnaireResponseItem, QuestionnaireResponseItemAnswer } from 'fhir/r4';
19
- import { nanoid } from 'nanoid';
20
19
  import { useMemo } from 'react';
20
+ import { generateExistingRepeatId, generateNewRepeatId } from '../utils/repeatId';
21
21
 
22
22
  function useInitialiseRepeatAnswers(
23
+ linkId: string,
23
24
  qrItem: QuestionnaireResponseItem | null
24
25
  ): (QuestionnaireResponseItemAnswer | null)[] {
25
26
  return useMemo(() => {
26
- let initialRepeatAnswers: (QuestionnaireResponseItemAnswer | null)[] = [{ id: nanoid() }];
27
-
28
- if (qrItem?.answer) {
29
- initialRepeatAnswers = qrItem.answer.map((answer) => {
30
- if (!answer.id) {
31
- answer.id = nanoid();
32
- }
33
- return answer;
34
- });
27
+ if (!qrItem?.answer) {
28
+ return [{ id: generateNewRepeatId(linkId) }];
35
29
  }
36
30
 
37
- return initialRepeatAnswers;
38
- }, [qrItem]);
31
+ return qrItem.answer.map((answer, index) => ({
32
+ ...answer,
33
+ id: answer.id ?? generateExistingRepeatId(linkId, index)
34
+ }));
35
+ }, [linkId, qrItem]);
39
36
  }
40
37
 
41
38
  export default useInitialiseRepeatAnswers;
@@ -15,38 +15,27 @@
15
15
  * limitations under the License.
16
16
  */
17
17
 
18
- import type { QuestionnaireItem, QuestionnaireResponseItem } from 'fhir/r4';
19
- import { nanoid } from 'nanoid';
18
+ import type { QuestionnaireResponseItem } from 'fhir/r4';
20
19
  import type { RepeatGroupSingle } from '../interfaces/repeatGroup.interface';
21
20
  import { useMemo } from 'react';
21
+ import { generateExistingRepeatId, generateNewRepeatId } from '../utils/repeatId';
22
22
 
23
23
  function useInitialiseRepeatGroups(
24
- qItem: QuestionnaireItem,
24
+ linkId: string,
25
25
  qrItems: QuestionnaireResponseItem[]
26
26
  ): RepeatGroupSingle[] {
27
- return useMemo(
28
- () => {
29
- let initialRepeatGroupAnswers: RepeatGroupSingle[] = [
30
- {
31
- nanoId: nanoid(),
32
- qrItem: null
33
- }
34
- ];
27
+ return useMemo(() => {
28
+ if (qrItems.length === 0) {
29
+ return [{ id: generateNewRepeatId(linkId), qrItem: null }];
30
+ }
35
31
 
36
- if (qrItems.length > 0) {
37
- initialRepeatGroupAnswers = qrItems.map((qrItem) => {
38
- return {
39
- nanoId: nanoid(),
40
- qrItem
41
- };
42
- });
43
- }
44
- return initialRepeatGroupAnswers;
45
- },
46
- // Requires checking of both qItem and qrItems
47
- // eslint-disable-next-line react-hooks/exhaustive-deps
48
- [qItem, qrItems]
49
- );
32
+ return qrItems.map((qrItem, index) => {
33
+ return {
34
+ id: generateExistingRepeatId(linkId, index),
35
+ qrItem
36
+ };
37
+ });
38
+ }, [linkId, qrItems]);
50
39
  }
51
40
 
52
41
  export default useInitialiseRepeatGroups;
@@ -19,6 +19,7 @@ import type { Dispatch, SetStateAction } from 'react';
19
19
  import { useEffect, useState } from 'react';
20
20
  import type { RepeatGroupSingle } from '../interfaces/repeatGroup.interface';
21
21
  import _isEqual from 'lodash/isEqual';
22
+ import type { QuestionnaireResponseItem } from 'fhir/r4';
22
23
 
23
24
  function useRepeatGroups(
24
25
  valueFromProps: RepeatGroupSingle[]
@@ -27,10 +28,13 @@ function useRepeatGroups(
27
28
 
28
29
  useEffect(
29
30
  () => {
30
- const valueFromPropsQRItems = valueFromProps.map(
31
- (repeatGroupSingle) => repeatGroupSingle.qrItem
32
- );
33
- const repeatGroupsQRItems = repeatGroups.map((repeatGroupSingle) => repeatGroupSingle.qrItem);
31
+ const valueFromPropsQRItems = valueFromProps
32
+ .map((repeatGroupSingle) => repeatGroupSingle.qrItem)
33
+ .filter((qrItem): qrItem is QuestionnaireResponseItem => qrItem !== null);
34
+
35
+ const repeatGroupsQRItems = repeatGroups
36
+ .map((repeatGroupSingle) => repeatGroupSingle.qrItem)
37
+ .filter((qrItem): qrItem is QuestionnaireResponseItem => qrItem !== null);
34
38
 
35
39
  if (!_isEqual(valueFromPropsQRItems, repeatGroupsQRItems)) {
36
40
  setRepeatGroups(valueFromProps);
package/src/index.ts CHANGED
@@ -42,6 +42,7 @@ export {
42
42
  destroyForm,
43
43
  getResponse,
44
44
  removeEmptyAnswersFromResponse,
45
+ removeInternalIdsFromResponse,
45
46
  isSpecificItemControl,
46
47
  isRepeatItemAndNotCheckbox,
47
48
  initialiseQuestionnaireResponse,
@@ -18,6 +18,6 @@
18
18
  import type { QuestionnaireResponseItem } from 'fhir/r4';
19
19
 
20
20
  export interface GroupTableRowModel {
21
- nanoId: string;
21
+ id: string;
22
22
  qrItem: QuestionnaireResponseItem | null;
23
23
  }
@@ -23,6 +23,6 @@ export interface QrRepeatGroup {
23
23
  }
24
24
 
25
25
  export interface RepeatGroupSingle {
26
- nanoId: string;
26
+ id: string;
27
27
  qrItem: QuestionnaireResponseItem | null;
28
28
  }
@@ -0,0 +1,161 @@
1
+ /*
2
+ * Copyright 2024 Commonwealth Scientific and Industrial Research
3
+ * Organisation (CSIRO) ABN 41 687 119 230.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ import type { Questionnaire } from 'fhir/r4';
19
+
20
+ export const qMyPatient: Questionnaire = {
21
+ resourceType: 'Questionnaire',
22
+ id: 'canshare-myPatient1',
23
+ meta: {
24
+ versionId: '9',
25
+ lastUpdated: '2024-09-18T07:23:35.7317908+00:00'
26
+ },
27
+ extension: [
28
+ {
29
+ extension: [
30
+ {
31
+ url: 'name',
32
+ valueId: 'LaunchPatient'
33
+ },
34
+ {
35
+ url: 'type',
36
+ valueCode: 'Patient'
37
+ },
38
+ {
39
+ url: 'description',
40
+ valueString: 'The patient that is to be used to pre-populate the form'
41
+ }
42
+ ],
43
+ url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-launchContext'
44
+ },
45
+ {
46
+ extension: [
47
+ {
48
+ url: 'name',
49
+ valueId: 'LaunchPractitioner'
50
+ },
51
+ {
52
+ url: 'type',
53
+ valueCode: 'Practitioner'
54
+ },
55
+ {
56
+ url: 'description',
57
+ valueString: 'The practitioner that is to be used to pre-populate the form'
58
+ }
59
+ ],
60
+ url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-launchContext'
61
+ },
62
+ {
63
+ url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-itemExtractionContext',
64
+ valueCode: 'Patient'
65
+ }
66
+ ],
67
+ url: 'http://canshare.co.nz/questionnaire/myPatient1',
68
+ name: 'myPatient1',
69
+ status: 'active',
70
+ publisher: 'DEMO: David Hay',
71
+ useContext: [
72
+ {
73
+ code: {
74
+ system: 'http://terminology.hl7.org/CodeSystem/usage-context-type',
75
+ code: 'user',
76
+ display: 'User Type'
77
+ },
78
+ valueCodeableConcept: {
79
+ coding: [
80
+ {
81
+ code: 'extract',
82
+ display: 'Demo Extract'
83
+ }
84
+ ]
85
+ }
86
+ }
87
+ ],
88
+ item: [
89
+ {
90
+ linkId: 'myPatient1',
91
+ text: 'myPatient1',
92
+ type: 'group',
93
+ item: [
94
+ {
95
+ linkId: 'myPatient1.name',
96
+ definition: 'http://hl7.org/fhir/StructureDefinition/Patient#Patient.name',
97
+ text: 'name *',
98
+ type: 'group',
99
+ repeats: true,
100
+ item: [
101
+ {
102
+ linkId: 'myPatient1.name.first',
103
+ definition: 'http://hl7.org/fhir/StructureDefinition/Patient#Patient.name.given',
104
+ text: 'firstName *',
105
+ type: 'string',
106
+ repeats: true
107
+ },
108
+ {
109
+ linkId: 'myPatient1.name.lastName',
110
+ definition: 'http://hl7.org/fhir/StructureDefinition/Patient#Patient.name.family',
111
+ text: 'lastName',
112
+ type: 'string'
113
+ }
114
+ ]
115
+ },
116
+ {
117
+ linkId: 'myPatient1.hair',
118
+ definition: 'http://hl7.org/fhir/StructureDefinition/Patient#Patient.extension',
119
+ text: 'hair',
120
+ type: 'group',
121
+ item: [
122
+ {
123
+ linkId: 'myPatient1.hair.colour',
124
+ definition:
125
+ 'http://hl7.org/fhir/StructureDefinition/Patient#Patient.extension.valueString',
126
+ text: 'colour',
127
+ type: 'string'
128
+ },
129
+ {
130
+ linkId: 'myPatient1.hair.url',
131
+ definition: 'http://hl7.org/fhir/StructureDefinition/Patient#Patient.extension.url',
132
+ text: 'url',
133
+ type: 'string'
134
+ }
135
+ ]
136
+ },
137
+ {
138
+ linkId: 'myPatient1.religion',
139
+ definition: 'http://hl7.org/fhir/StructureDefinition/Patient#Patient.extension',
140
+ text: 'religion',
141
+ type: 'group',
142
+ item: [
143
+ {
144
+ linkId: 'myPatient1.religion.brand',
145
+ definition:
146
+ 'http://hl7.org/fhir/StructureDefinition/Patient#Patient.extension.valueString',
147
+ text: 'brand',
148
+ type: 'string'
149
+ },
150
+ {
151
+ linkId: 'myPatient1.religion.url1',
152
+ definition: 'http://hl7.org/fhir/StructureDefinition/Patient#Patient.extension.url',
153
+ text: 'url',
154
+ type: 'string'
155
+ }
156
+ ]
157
+ }
158
+ ]
159
+ }
160
+ ]
161
+ };
@@ -44,7 +44,7 @@ function BuildFormButtonForStorybook(props: BuildFormButtonProps) {
44
44
  <Box display="flex" mb={0.5} alignItems="center" columnGap={3}>
45
45
  <Tooltip title="Build form with questionnaire response" placement="right">
46
46
  <IconButton onClick={handleBuildForm} size="small" color="primary">
47
- <Iconify icon="ph:hammer" sx={{ mb: 0.5 }} />
47
+ <Iconify icon="ph:hammer" />
48
48
  </IconButton>
49
49
  </Tooltip>
50
50
  </Box>
@@ -0,0 +1,51 @@
1
+ /*
2
+ * Copyright 2024 Commonwealth Scientific and Industrial Research
3
+ * Organisation (CSIRO) ABN 41 687 119 230.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ // @ts-ignore
19
+ import React from 'react';
20
+ import type { Questionnaire, QuestionnaireResponse } from 'fhir/r4';
21
+ import { Box, IconButton, Tooltip } from '@mui/material';
22
+ import ContentCutIcon from '@mui/icons-material/ContentCut';
23
+ import { useQuestionnaireResponseStore } from '../../stores';
24
+ import { removeInternalIdsFromResponse } from '../../utils/manageForm';
25
+
26
+ interface IdRemoverButtonProps {
27
+ questionnaire: Questionnaire;
28
+ questionnaireResponse: QuestionnaireResponse;
29
+ }
30
+
31
+ function IdRemoverButtonForStorybook(props: IdRemoverButtonProps) {
32
+ const { questionnaire, questionnaireResponse } = props;
33
+
34
+ const updateResponse = useQuestionnaireResponseStore.use.updateResponse();
35
+
36
+ async function handleRemoveIds() {
37
+ updateResponse(removeInternalIdsFromResponse(questionnaire, questionnaireResponse));
38
+ }
39
+
40
+ return (
41
+ <Box display="flex" mb={0.5} alignItems="center" columnGap={3}>
42
+ <Tooltip title="Remove IDs from questionnaire response" placement="right">
43
+ <IconButton onClick={handleRemoveIds} size="small" color="primary">
44
+ <ContentCutIcon />
45
+ </IconButton>
46
+ </Tooltip>
47
+ </Box>
48
+ );
49
+ }
50
+
51
+ export default IdRemoverButtonForStorybook;