@aehrc/smart-forms-renderer 0.27.2 → 0.27.4

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 (179) hide show
  1. package/.storybook/main.ts +2 -1
  2. package/.storybook/preview.ts +6 -1
  3. package/lib/components/FormComponents/ChoiceItems/ChoiceCheckboxAnswerOptionFields.d.ts +3 -2
  4. package/lib/components/FormComponents/ChoiceItems/ChoiceCheckboxAnswerOptionFields.js +9 -17
  5. package/lib/components/FormComponents/ChoiceItems/ChoiceCheckboxAnswerOptionFields.js.map +1 -1
  6. package/lib/components/FormComponents/ChoiceItems/ChoiceCheckboxAnswerOptionItem.js +12 -9
  7. package/lib/components/FormComponents/ChoiceItems/ChoiceCheckboxAnswerOptionItem.js.map +1 -1
  8. package/lib/components/FormComponents/ChoiceItems/ChoiceCheckboxAnswerValueSetFields.d.ts +2 -2
  9. package/lib/components/FormComponents/ChoiceItems/ChoiceCheckboxAnswerValueSetFields.js +7 -9
  10. package/lib/components/FormComponents/ChoiceItems/ChoiceCheckboxAnswerValueSetFields.js.map +1 -1
  11. package/lib/components/FormComponents/ChoiceItems/ChoiceCheckboxAnswerValueSetItem.js +12 -8
  12. package/lib/components/FormComponents/ChoiceItems/ChoiceCheckboxAnswerValueSetItem.js.map +1 -1
  13. package/lib/components/FormComponents/ChoiceItems/ChoiceRadioAnswerOptionFields.d.ts +2 -1
  14. package/lib/components/FormComponents/ChoiceItems/ChoiceRadioAnswerOptionFields.js +3 -3
  15. package/lib/components/FormComponents/ChoiceItems/ChoiceRadioAnswerOptionFields.js.map +1 -1
  16. package/lib/components/FormComponents/ChoiceItems/ChoiceRadioAnswerOptionItem.js +6 -4
  17. package/lib/components/FormComponents/ChoiceItems/ChoiceRadioAnswerOptionItem.js.map +1 -1
  18. package/lib/components/FormComponents/ChoiceItems/ChoiceRadioAnswerOptionView.d.ts +2 -1
  19. package/lib/components/FormComponents/ChoiceItems/ChoiceRadioAnswerOptionView.js +3 -3
  20. package/lib/components/FormComponents/ChoiceItems/ChoiceRadioAnswerOptionView.js.map +1 -1
  21. package/lib/components/FormComponents/ChoiceItems/ChoiceRadioAnswerValueSetFields.d.ts +2 -2
  22. package/lib/components/FormComponents/ChoiceItems/ChoiceRadioAnswerValueSetFields.js +5 -7
  23. package/lib/components/FormComponents/ChoiceItems/ChoiceRadioAnswerValueSetFields.js.map +1 -1
  24. package/lib/components/FormComponents/ChoiceItems/ChoiceRadioAnswerValueSetItem.js +4 -4
  25. package/lib/components/FormComponents/ChoiceItems/ChoiceRadioAnswerValueSetItem.js.map +1 -1
  26. package/lib/components/FormComponents/ChoiceItems/ChoiceSelectAnswerOptionFields.d.ts +2 -1
  27. package/lib/components/FormComponents/ChoiceItems/ChoiceSelectAnswerOptionFields.js +2 -3
  28. package/lib/components/FormComponents/ChoiceItems/ChoiceSelectAnswerOptionFields.js.map +1 -1
  29. package/lib/components/FormComponents/ChoiceItems/ChoiceSelectAnswerOptionItem.js +5 -3
  30. package/lib/components/FormComponents/ChoiceItems/ChoiceSelectAnswerOptionItem.js.map +1 -1
  31. package/lib/components/FormComponents/ChoiceItems/ChoiceSelectAnswerOptionView.d.ts +2 -1
  32. package/lib/components/FormComponents/ChoiceItems/ChoiceSelectAnswerOptionView.js +3 -3
  33. package/lib/components/FormComponents/ChoiceItems/ChoiceSelectAnswerOptionView.js.map +1 -1
  34. package/lib/components/FormComponents/DecimalItem/DecimalItem.js +1 -1
  35. package/lib/components/FormComponents/GroupItem/TabButtonsWrapper.js +20 -20
  36. package/lib/components/FormComponents/GroupItem/TabButtonsWrapper.js.map +1 -1
  37. package/lib/components/FormComponents/OpenChoiceItems/OpenChoiceCheckboxAnswerOptionFields.d.ts +3 -2
  38. package/lib/components/FormComponents/OpenChoiceItems/OpenChoiceCheckboxAnswerOptionFields.js +7 -19
  39. package/lib/components/FormComponents/OpenChoiceItems/OpenChoiceCheckboxAnswerOptionFields.js.map +1 -1
  40. package/lib/components/FormComponents/OpenChoiceItems/OpenChoiceCheckboxAnswerOptionItem.js +40 -40
  41. package/lib/components/FormComponents/OpenChoiceItems/OpenChoiceCheckboxAnswerOptionItem.js.map +1 -1
  42. package/lib/components/FormComponents/OpenChoiceItems/OpenChoiceItemSwitcher.js +15 -3
  43. package/lib/components/FormComponents/OpenChoiceItems/OpenChoiceItemSwitcher.js.map +1 -1
  44. package/lib/components/FormComponents/OpenChoiceItems/OpenChoiceRadioAnswerOptionFields.d.ts +2 -1
  45. package/lib/components/FormComponents/OpenChoiceItems/OpenChoiceRadioAnswerOptionFields.js +3 -3
  46. package/lib/components/FormComponents/OpenChoiceItems/OpenChoiceRadioAnswerOptionFields.js.map +1 -1
  47. package/lib/components/FormComponents/OpenChoiceItems/OpenChoiceRadioAnswerOptionItem.js +27 -26
  48. package/lib/components/FormComponents/OpenChoiceItems/OpenChoiceRadioAnswerOptionItem.js.map +1 -1
  49. package/lib/components/FormComponents/StringItem/StringField.js +1 -1
  50. package/lib/components/FormComponents/StringItem/StringField.js.map +1 -1
  51. package/lib/hooks/useInitialiseRenderer.js +1 -1
  52. package/lib/hooks/useInitialiseRenderer.js.map +1 -1
  53. package/lib/hooks/useNextAndPreviousVisibleTabs.d.ts +7 -0
  54. package/lib/hooks/useNextAndPreviousVisibleTabs.js +63 -0
  55. package/lib/hooks/useNextAndPreviousVisibleTabs.js.map +1 -0
  56. package/lib/hooks/useNextPreviousVisibleTabs.d.ts +6 -0
  57. package/lib/hooks/useNextPreviousVisibleTabs.js +63 -0
  58. package/lib/hooks/useNextPreviousVisibleTabs.js.map +1 -0
  59. package/lib/index.d.ts +0 -7
  60. package/lib/index.js +0 -24
  61. package/lib/index.js.map +1 -1
  62. package/lib/utils/choice.d.ts +1 -7
  63. package/lib/utils/choice.js +10 -20
  64. package/lib/utils/choice.js.map +1 -1
  65. package/lib/utils/enableWhen.js +5 -7
  66. package/lib/utils/enableWhen.js.map +1 -1
  67. package/lib/utils/index.d.ts +1 -0
  68. package/lib/utils/index.js +1 -0
  69. package/lib/utils/index.js.map +1 -1
  70. package/lib/utils/openChoice.d.ts +9 -4
  71. package/lib/utils/openChoice.js +47 -78
  72. package/lib/utils/openChoice.js.map +1 -1
  73. package/lib/utils/tabs.d.ts +0 -21
  74. package/lib/utils/tabs.js +0 -51
  75. package/lib/utils/tabs.js.map +1 -1
  76. package/package.json +4 -4
  77. package/src/components/FormComponents/ChoiceItems/CheckboxOptionList.tsx +82 -0
  78. package/src/components/FormComponents/ChoiceItems/ChoiceCheckboxAnswerOptionFields.tsx +23 -52
  79. package/src/components/FormComponents/ChoiceItems/ChoiceCheckboxAnswerOptionItem.tsx +15 -9
  80. package/src/components/FormComponents/ChoiceItems/ChoiceCheckboxAnswerValueSetFields.tsx +17 -19
  81. package/src/components/FormComponents/ChoiceItems/ChoiceCheckboxAnswerValueSetItem.tsx +13 -8
  82. package/src/components/FormComponents/ChoiceItems/ChoiceRadioAnswerOptionFields.tsx +5 -4
  83. package/src/components/FormComponents/ChoiceItems/ChoiceRadioAnswerOptionItem.tsx +6 -2
  84. package/src/components/FormComponents/ChoiceItems/ChoiceRadioAnswerOptionView.tsx +5 -1
  85. package/src/components/FormComponents/ChoiceItems/ChoiceRadioAnswerValueSetFields.tsx +9 -16
  86. package/src/components/FormComponents/ChoiceItems/ChoiceRadioAnswerValueSetItem.tsx +4 -4
  87. package/src/components/FormComponents/ChoiceItems/ChoiceSelectAnswerOptionFields.tsx +4 -3
  88. package/src/components/FormComponents/ChoiceItems/ChoiceSelectAnswerOptionItem.tsx +5 -2
  89. package/src/components/FormComponents/ChoiceItems/ChoiceSelectAnswerOptionView.tsx +5 -1
  90. package/src/components/FormComponents/DecimalItem/DecimalItem.tsx +1 -1
  91. package/src/components/FormComponents/GroupItem/TabButtonsWrapper.tsx +28 -25
  92. package/src/components/FormComponents/ItemParts/{RadioAnswerOptionButtons.tsx → RadioOptionList.tsx} +7 -7
  93. package/src/components/FormComponents/OpenChoiceItems/OpenChoiceCheckboxAnswerOptionFields.tsx +18 -50
  94. package/src/components/FormComponents/OpenChoiceItems/OpenChoiceCheckboxAnswerOptionItem.tsx +70 -68
  95. package/src/components/FormComponents/OpenChoiceItems/OpenChoiceCheckboxAnswerValueSetFields.tsx +110 -0
  96. package/src/components/FormComponents/OpenChoiceItems/OpenChoiceCheckboxAnswerValueSetItem.tsx +188 -0
  97. package/src/components/FormComponents/OpenChoiceItems/OpenChoiceItemSwitcher.tsx +46 -19
  98. package/src/components/FormComponents/OpenChoiceItems/OpenChoiceRadioAnswerOptionFields.tsx +5 -3
  99. package/src/components/FormComponents/OpenChoiceItems/OpenChoiceRadioAnswerOptionItem.tsx +37 -29
  100. package/src/components/FormComponents/OpenChoiceItems/OpenChoiceRadioAnswerValueSetFields.tsx +104 -0
  101. package/src/components/FormComponents/OpenChoiceItems/OpenChoiceRadioAnswerValueSetItem.tsx +156 -0
  102. package/src/components/FormComponents/StringItem/StringField.tsx +1 -1
  103. package/src/hooks/useInitialiseRenderer.ts +1 -1
  104. package/src/hooks/useNextAndPreviousVisibleTabs.ts +86 -0
  105. package/src/hooks/useOpenLabel.ts +43 -0
  106. package/src/index.ts +0 -21
  107. package/src/stories/BuildFormWrapper.tsx +57 -0
  108. package/src/stories/assets/questionnaires/QAdvancedAdditionalDisplayContent.ts +83 -0
  109. package/src/stories/assets/questionnaires/QAdvancedControlAppearance.ts +294 -0
  110. package/src/stories/assets/questionnaires/QAdvancedOther.ts +495 -0
  111. package/src/stories/assets/questionnaires/QAdvancedTextApperance.ts +188 -0
  112. package/src/stories/assets/questionnaires/QAttachment.ts +38 -0
  113. package/src/stories/assets/questionnaires/QBehaviorCalculations.ts +645 -0
  114. package/src/stories/assets/questionnaires/QBehaviorChoiceRestriction.ts +281 -0
  115. package/src/stories/assets/questionnaires/QBehaviorOther.ts +1149 -0
  116. package/src/stories/assets/questionnaires/QBehaviorValueConstraints.ts +508 -0
  117. package/src/stories/assets/questionnaires/QBoolean.ts +130 -0
  118. package/src/stories/assets/questionnaires/QChoice.ts +137 -0
  119. package/src/stories/assets/questionnaires/QDate.ts +56 -0
  120. package/src/stories/assets/questionnaires/QDateTime.ts +56 -0
  121. package/src/stories/assets/questionnaires/QDecimal.ts +56 -0
  122. package/src/stories/assets/questionnaires/QDisplay.ts +38 -0
  123. package/src/stories/assets/questionnaires/QGroup.ts +52 -0
  124. package/src/stories/assets/questionnaires/QInteger.ts +119 -0
  125. package/src/stories/assets/questionnaires/QItemControlDisplay.ts +114 -0
  126. package/src/stories/assets/questionnaires/QItemControlGroup.ts +419 -0
  127. package/src/stories/assets/questionnaires/QItemControlQuestion.ts +1271 -0
  128. package/src/stories/assets/questionnaires/QOpenChoice.ts +151 -0
  129. package/src/stories/assets/questionnaires/QQuantity.ts +38 -0
  130. package/src/stories/assets/questionnaires/QReference.ts +38 -0
  131. package/src/stories/assets/questionnaires/QSingleItems.ts +251 -0
  132. package/src/stories/assets/questionnaires/QString.ts +131 -0
  133. package/src/stories/assets/questionnaires/QText.ts +169 -0
  134. package/src/stories/assets/questionnaires/QTime.ts +38 -0
  135. package/src/stories/assets/questionnaires/QUrl.ts +38 -0
  136. package/src/stories/assets/questionnaires/index.ts +44 -0
  137. package/src/stories/itemTypes/Attachment.stories.tsx +39 -0
  138. package/src/stories/itemTypes/Boolean.stories.tsx +72 -0
  139. package/src/stories/{MedicalHistoryTable.stories.tsx → itemTypes/Choice.stories.tsx} +32 -26
  140. package/src/stories/itemTypes/Date.stories.tsx +46 -0
  141. package/src/stories/itemTypes/DateTime.stories.tsx +45 -0
  142. package/src/stories/itemTypes/Decimal.stories.tsx +56 -0
  143. package/src/stories/itemTypes/Display.stories.tsx +39 -0
  144. package/src/stories/itemTypes/Group.stories.tsx +39 -0
  145. package/src/stories/itemTypes/Integer.stories.tsx +55 -0
  146. package/src/stories/itemTypes/OpenChoice.stories.tsx +64 -0
  147. package/src/stories/itemTypes/Quantity.stories.tsx +39 -0
  148. package/src/stories/itemTypes/Reference.stories.tsx +39 -0
  149. package/src/stories/itemTypes/String.stories.tsx +51 -0
  150. package/src/stories/itemTypes/Text.stories.tsx +51 -0
  151. package/src/stories/itemTypes/Time.stories.tsx +39 -0
  152. package/src/stories/itemTypes/Url.stories.tsx +39 -0
  153. package/src/stories/sdc/AdvancedAdditionalDisplayContent.stories.tsx +45 -0
  154. package/src/stories/sdc/AdvancedControlAppearance.stories.tsx +51 -0
  155. package/src/stories/sdc/AdvancedOther.stories.tsx +76 -0
  156. package/src/stories/sdc/AdvancedTextAppearance.stories.tsx +69 -0
  157. package/src/stories/sdc/BehaviorCalculations.stories.tsx +69 -0
  158. package/src/stories/sdc/BehaviorChoiceRestriction.stories.tsx +76 -0
  159. package/src/stories/sdc/BehaviorOther.stories.tsx +90 -0
  160. package/src/stories/sdc/BehaviorValueConstraints.stories.tsx +88 -0
  161. package/src/stories/sdc/ItemControlDisplay.stories.tsx +39 -0
  162. package/src/stories/sdc/ItemControlGroup.stories.tsx +55 -0
  163. package/src/stories/sdc/ItemControlQuestion.stories.tsx +118 -0
  164. package/src/utils/buildForm.ts +23 -0
  165. package/src/utils/choice.ts +16 -23
  166. package/src/utils/enableWhen.ts +5 -7
  167. package/src/utils/index.ts +1 -0
  168. package/src/utils/openChoice.ts +83 -98
  169. package/src/utils/tabs.ts +0 -75
  170. package/vite.config.ts +23 -0
  171. package/doctor-storybook.log +0 -18
  172. package/src/stories/SmartFormsRenderer.stories.ts +0 -139
  173. package/src/stories/assets/QItems-and-QRItems/QR_GTableMedicalHistory.json +0 -80
  174. package/src/stories/assets/QItems-and-QRItems/Q_GTableMedicalHistory.json +0 -109
  175. package/src/stories/assets/Qs-and-QRs/Q715.json +0 -15086
  176. package/src/stories/assets/Qs-and-QRs/QDev715.json +0 -16081
  177. package/src/stories/assets/Qs-and-QRs/QTestGrid.json +0 -411
  178. package/src/stories/assets/Qs-and-QRs/R715.json +0 -311
  179. package/src/stories/assets/Qs-and-QRs/RTestGrid.json +0 -34
@@ -15,15 +15,11 @@
15
15
  * limitations under the License.
16
16
  */
17
17
 
18
- import React, { useCallback, useMemo, useState } from 'react';
19
- import { CheckBoxOption } from '../../../interfaces/choice.enum';
18
+ import React, { useCallback } from 'react';
20
19
  import type { QuestionnaireItem, QuestionnaireResponseItem } from 'fhir/r4';
21
20
  import { createEmptyQrItem } from '../../../utils/qrItem';
22
21
  import { getOpenLabelText } from '../../../utils/itemControl';
23
- import {
24
- getOldOpenLabelAnswer,
25
- updateQrOpenChoiceCheckboxAnswers
26
- } from '../../../utils/openChoice';
22
+ import { updateOpenLabelAnswer } from '../../../utils/openChoice';
27
23
  import { FullWidthFormComponentBox } from '../../Box.styles';
28
24
  import debounce from 'lodash.debounce';
29
25
  import useRenderingExtensions from '../../../hooks/useRenderingExtensions';
@@ -35,10 +31,12 @@ import type {
35
31
  } from '../../../interfaces/renderProps.interface';
36
32
  import { DEBOUNCE_DURATION } from '../../../utils/debounce';
37
33
  import DisplayInstructions from '../DisplayItem/DisplayInstructions';
38
- import OpenChoiceCheckboxAnswerOptionFields from './OpenChoiceCheckboxAnswerOptionFields';
39
34
  import useReadOnly from '../../../hooks/useReadOnly';
40
35
  import ItemFieldGrid from '../ItemParts/ItemFieldGrid';
41
36
  import { useQuestionnaireStore } from '../../../stores';
37
+ import useOpenLabel from '../../../hooks/useOpenLabel';
38
+ import { updateChoiceCheckboxAnswers } from '../../../utils/choice';
39
+ import OpenChoiceCheckboxAnswerOptionFields from './OpenChoiceCheckboxAnswerOptionFields';
42
40
 
43
41
  interface OpenChoiceCheckboxAnswerOptionItemProps
44
42
  extends PropsWithQrItemChangeHandler,
@@ -61,92 +59,95 @@ function OpenChoiceCheckboxAnswerOptionItem(props: OpenChoiceCheckboxAnswerOptio
61
59
 
62
60
  const onFocusLinkId = useQuestionnaireStore.use.onFocusLinkId();
63
61
 
62
+ // Init input value
63
+ const qrOpenChoiceCheckbox = qrItem ?? createEmptyQrItem(qItem);
64
+ const answers = qrOpenChoiceCheckbox.answer ?? [];
65
+
64
66
  const readOnly = useReadOnly(qItem, parentIsReadOnly);
65
- const openLabelText = getOpenLabelText(qItem);
66
67
  const { displayInstructions } = useRenderingExtensions(qItem);
68
+ const openLabelText = getOpenLabelText(qItem);
67
69
 
68
- // Init answers
69
- const qrOpenChoiceCheckbox = qrItem ?? createEmptyQrItem(qItem);
70
- const answers = useMemo(() => qrOpenChoiceCheckbox.answer ?? [], [qrOpenChoiceCheckbox.answer]);
71
-
72
- // Init options and open label value
73
- const answerOptions = qItem.answerOption;
74
- let initialOpenLabelValue = '';
75
- let initialOpenLabelChecked = false;
76
- if (answerOptions) {
77
- const oldLabelAnswer = getOldOpenLabelAnswer(answers, answerOptions);
78
- if (oldLabelAnswer && oldLabelAnswer.valueString) {
79
- initialOpenLabelValue = oldLabelAnswer.valueString;
80
- initialOpenLabelChecked = true;
70
+ const options = qItem.answerOption ?? [];
71
+
72
+ const { openLabelValue, setOpenLabelValue, openLabelChecked, setOpenLabelChecked } = useOpenLabel(
73
+ options,
74
+ answers
75
+ );
76
+
77
+ // Event handlers
78
+
79
+ // One of the options is changed
80
+ // Processing is similar to a choice checkbox
81
+ function handleOptionChange(changedOptionValue: string) {
82
+ if (options.length === 0) {
83
+ onQrItemChange(createEmptyQrItem(qItem));
84
+ return;
85
+ }
86
+
87
+ // Process as a choice checkbox
88
+ const updatedQrItem = updateChoiceCheckboxAnswers(
89
+ changedOptionValue,
90
+ answers,
91
+ options,
92
+ qrOpenChoiceCheckbox,
93
+ isRepeated
94
+ );
95
+
96
+ if (updatedQrItem) {
97
+ onQrItemChange(updatedQrItem);
98
+ }
99
+
100
+ // If single-selection, uncheck open label
101
+ if (!isRepeated) {
102
+ setOpenLabelChecked(false);
81
103
  }
82
104
  }
83
- const [openLabelValue, setOpenLabelValue] = useState(initialOpenLabelValue);
84
- const [openLabelChecked, setOpenLabelChecked] = useState(initialOpenLabelChecked);
85
105
 
86
- // Event handlers
87
- const handleValueChange = useCallback(
88
- (changedOptionValue: string | null, changedOpenLabelValue: string | null) => {
89
- if (!answerOptions) return null;
90
-
91
- let updatedQrChoiceCheckbox: QuestionnaireResponseItem | null = null;
92
- if (changedOptionValue) {
93
- updatedQrChoiceCheckbox = updateQrOpenChoiceCheckboxAnswers(
94
- changedOptionValue,
95
- null,
96
- answers,
97
- answerOptions,
98
- qrOpenChoiceCheckbox,
99
- CheckBoxOption.AnswerOption,
100
- isRepeated
101
- );
102
- } else if (changedOpenLabelValue !== null) {
103
- updatedQrChoiceCheckbox = updateQrOpenChoiceCheckboxAnswers(
104
- null,
105
- changedOpenLabelValue,
106
- answers,
107
- answerOptions,
108
- qrOpenChoiceCheckbox,
109
- CheckBoxOption.AnswerOption,
110
- isRepeated
111
- );
112
- }
113
-
114
- if (updatedQrChoiceCheckbox) {
115
- onQrItemChange(updatedQrChoiceCheckbox);
116
- }
117
- },
118
- [answerOptions, answers, isRepeated, onQrItemChange, qrOpenChoiceCheckbox]
119
- );
106
+ function handleOpenLabelChange(openLabelChecked: boolean, changedOpenLabelValue: string) {
107
+ const updatedQrItem = updateOpenLabelAnswer(
108
+ openLabelChecked,
109
+ changedOpenLabelValue,
110
+ answers,
111
+ options,
112
+ qrOpenChoiceCheckbox,
113
+ isRepeated
114
+ );
120
115
 
121
- // eslint-disable-next-line react-hooks/exhaustive-deps
122
- const updateOpenLabelValueWithDebounce = useCallback(
123
- debounce((input: string) => {
124
- handleValueChange(null, input);
125
- }, DEBOUNCE_DURATION),
126
- [handleValueChange]
127
- ); // Dependencies are tested, debounce is causing eslint to not recognise dependencies
116
+ if (updatedQrItem) {
117
+ onQrItemChange(updatedQrItem);
118
+ }
119
+ }
128
120
 
129
121
  function handleOpenLabelCheckedChange(checked: boolean) {
130
- handleValueChange(null, openLabelValue);
122
+ handleOpenLabelChange(checked, openLabelValue);
131
123
  setOpenLabelChecked(checked);
132
124
  }
133
125
 
134
126
  function handleOpenLabelInputChange(newValue: string) {
135
- setOpenLabelValue(newValue);
136
127
  updateOpenLabelValueWithDebounce(newValue);
128
+ setOpenLabelValue(newValue);
137
129
  }
138
130
 
131
+ // eslint-disable-next-line react-hooks/exhaustive-deps
132
+ const updateOpenLabelValueWithDebounce = useCallback(
133
+ debounce((input: string) => {
134
+ handleOpenLabelChange(openLabelChecked, input);
135
+ }, DEBOUNCE_DURATION),
136
+ [handleOpenLabelChange]
137
+ ); // Dependencies are tested, debounce is causing eslint to not recognise dependencies
138
+
139
139
  if (showMinimalView) {
140
140
  return (
141
141
  <>
142
142
  <OpenChoiceCheckboxAnswerOptionFields
143
143
  qItem={qItem}
144
+ options={options}
144
145
  answers={answers}
145
146
  openLabelText={openLabelText}
146
147
  openLabelValue={openLabelValue}
147
148
  openLabelChecked={openLabelChecked}
148
149
  readOnly={readOnly}
149
- onValueChange={handleValueChange}
150
+ onOptionChange={handleOptionChange}
150
151
  onOpenLabelCheckedChange={handleOpenLabelCheckedChange}
151
152
  onOpenLabelInputChange={handleOpenLabelInputChange}
152
153
  />
@@ -163,12 +164,13 @@ function OpenChoiceCheckboxAnswerOptionItem(props: OpenChoiceCheckboxAnswerOptio
163
164
  <ItemFieldGrid qItem={qItem} readOnly={readOnly}>
164
165
  <OpenChoiceCheckboxAnswerOptionFields
165
166
  qItem={qItem}
167
+ options={options}
166
168
  answers={answers}
167
169
  openLabelText={openLabelText}
168
170
  openLabelValue={openLabelValue}
169
171
  openLabelChecked={openLabelChecked}
170
172
  readOnly={readOnly}
171
- onValueChange={handleValueChange}
173
+ onOptionChange={handleOptionChange}
172
174
  onOpenLabelCheckedChange={handleOpenLabelCheckedChange}
173
175
  onOpenLabelInputChange={handleOpenLabelInputChange}
174
176
  />
@@ -0,0 +1,110 @@
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 React from 'react';
19
+ import { ChoiceItemOrientation } from '../../../interfaces/choice.enum';
20
+ import CheckboxSingleWithOpenLabel from '../ItemParts/CheckboxSingleWithOpenLabel';
21
+ import type {
22
+ QuestionnaireItem,
23
+ QuestionnaireItemAnswerOption,
24
+ QuestionnaireResponseItemAnswer
25
+ } from 'fhir/r4';
26
+ import { getChoiceOrientation } from '../../../utils/choice';
27
+ import { StyledFormGroup } from '../Item.styles';
28
+ import CheckboxOptionList from '../ChoiceItems/CheckboxOptionList';
29
+ import { StyledAlert } from '../../Alert.styles';
30
+ import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline';
31
+ import Typography from '@mui/material/Typography';
32
+ import type { TerminologyError } from '../../../hooks/useValueSetCodings';
33
+
34
+ interface OpenChoiceCheckboxFieldsProps {
35
+ qItem: QuestionnaireItem;
36
+ options: QuestionnaireItemAnswerOption[];
37
+ answers: QuestionnaireResponseItemAnswer[];
38
+ openLabelText: string | null;
39
+ openLabelValue: string;
40
+ openLabelChecked: boolean;
41
+ readOnly: boolean;
42
+ terminologyError: TerminologyError;
43
+ onOptionChange: (changedOptionValue: string) => void;
44
+ onOpenLabelCheckedChange: (checked: boolean) => void;
45
+ onOpenLabelInputChange: (input: string) => void;
46
+ }
47
+
48
+ function OpenChoiceCheckboxAnswerValueSetFields(props: OpenChoiceCheckboxFieldsProps) {
49
+ const {
50
+ qItem,
51
+ options,
52
+ answers,
53
+ openLabelText,
54
+ openLabelValue,
55
+ openLabelChecked,
56
+ readOnly,
57
+ terminologyError,
58
+ onOptionChange,
59
+ onOpenLabelCheckedChange,
60
+ onOpenLabelInputChange
61
+ } = props;
62
+
63
+ const orientation = getChoiceOrientation(qItem) ?? ChoiceItemOrientation.Vertical;
64
+
65
+ if (options.length > 0) {
66
+ return (
67
+ <StyledFormGroup row={orientation === ChoiceItemOrientation.Horizontal}>
68
+ <CheckboxOptionList
69
+ options={options}
70
+ answers={answers}
71
+ readOnly={readOnly}
72
+ onCheckedChange={onOptionChange}
73
+ />
74
+
75
+ {openLabelText !== null ? (
76
+ <CheckboxSingleWithOpenLabel
77
+ value={openLabelValue}
78
+ label={openLabelText}
79
+ isChecked={openLabelChecked}
80
+ onCheckedChange={onOpenLabelCheckedChange}
81
+ onInputChange={onOpenLabelInputChange}
82
+ />
83
+ ) : null}
84
+ </StyledFormGroup>
85
+ );
86
+ }
87
+
88
+ if (terminologyError.error) {
89
+ return (
90
+ <StyledAlert color="error">
91
+ <ErrorOutlineIcon color="error" sx={{ pr: 0.75 }} />
92
+ <Typography variant="subtitle2">
93
+ There was an error fetching options from the terminology server for{' '}
94
+ {terminologyError.answerValueSet}
95
+ </Typography>
96
+ </StyledAlert>
97
+ );
98
+ }
99
+
100
+ return (
101
+ <StyledAlert color="error">
102
+ <ErrorOutlineIcon color="error" sx={{ pr: 0.75 }} />
103
+ <Typography variant="subtitle2">
104
+ Unable to fetch options from the questionnaire or launch context
105
+ </Typography>
106
+ </StyledAlert>
107
+ );
108
+ }
109
+
110
+ export default OpenChoiceCheckboxAnswerValueSetFields;
@@ -0,0 +1,188 @@
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 React, { useCallback, useMemo } from 'react';
19
+ import type { QuestionnaireItem, QuestionnaireResponseItem } from 'fhir/r4';
20
+ import { createEmptyQrItem } from '../../../utils/qrItem';
21
+ import { getOpenLabelText } from '../../../utils/itemControl';
22
+ import { updateOpenLabelAnswer } from '../../../utils/openChoice';
23
+ import { FullWidthFormComponentBox } from '../../Box.styles';
24
+ import debounce from 'lodash.debounce';
25
+ import useRenderingExtensions from '../../../hooks/useRenderingExtensions';
26
+ import type {
27
+ PropsWithIsRepeatedAttribute,
28
+ PropsWithParentIsReadOnlyAttribute,
29
+ PropsWithQrItemChangeHandler,
30
+ PropsWithShowMinimalViewAttribute
31
+ } from '../../../interfaces/renderProps.interface';
32
+ import { DEBOUNCE_DURATION } from '../../../utils/debounce';
33
+ import DisplayInstructions from '../DisplayItem/DisplayInstructions';
34
+ import OpenChoiceCheckboxAnswerValueSetFields from './OpenChoiceCheckboxAnswerValueSetFields';
35
+ import useReadOnly from '../../../hooks/useReadOnly';
36
+ import ItemFieldGrid from '../ItemParts/ItemFieldGrid';
37
+ import { useQuestionnaireStore } from '../../../stores';
38
+ import useOpenLabel from '../../../hooks/useOpenLabel';
39
+ import { convertCodingsToAnswerOptions, updateChoiceCheckboxAnswers } from '../../../utils/choice';
40
+ import useValueSetCodings from '../../../hooks/useValueSetCodings';
41
+
42
+ interface OpenChoiceCheckboxAnswerValueSetItemProps
43
+ extends PropsWithQrItemChangeHandler,
44
+ PropsWithIsRepeatedAttribute,
45
+ PropsWithShowMinimalViewAttribute,
46
+ PropsWithParentIsReadOnlyAttribute {
47
+ qItem: QuestionnaireItem;
48
+ qrItem: QuestionnaireResponseItem | null;
49
+ }
50
+
51
+ function OpenChoiceCheckboxAnswerValueSetItem(props: OpenChoiceCheckboxAnswerValueSetItemProps) {
52
+ const {
53
+ qItem,
54
+ qrItem,
55
+ isRepeated,
56
+ showMinimalView = false,
57
+ parentIsReadOnly,
58
+ onQrItemChange
59
+ } = props;
60
+
61
+ const onFocusLinkId = useQuestionnaireStore.use.onFocusLinkId();
62
+
63
+ // Init input value
64
+ const qrOpenChoiceCheckbox = qrItem ?? createEmptyQrItem(qItem);
65
+ const answers = qrOpenChoiceCheckbox.answer ?? [];
66
+
67
+ const readOnly = useReadOnly(qItem, parentIsReadOnly);
68
+ const { displayInstructions } = useRenderingExtensions(qItem);
69
+ const openLabelText = getOpenLabelText(qItem);
70
+
71
+ // Get codings/options from valueSet
72
+ const { codings, terminologyError } = useValueSetCodings(qItem);
73
+
74
+ const options = useMemo(() => convertCodingsToAnswerOptions(codings), [codings]);
75
+
76
+ const { openLabelValue, setOpenLabelValue, openLabelChecked, setOpenLabelChecked } = useOpenLabel(
77
+ options,
78
+ answers
79
+ );
80
+
81
+ // Event handlers
82
+
83
+ // One of the options is changed
84
+ // Processing is similar to a choice checkbox
85
+ function handleOptionChange(changedOptionValue: string) {
86
+ if (options.length === 0) {
87
+ onQrItemChange(createEmptyQrItem(qItem));
88
+ return;
89
+ }
90
+
91
+ // Process as a choice checkbox
92
+ const updatedQrItem = updateChoiceCheckboxAnswers(
93
+ changedOptionValue,
94
+ answers,
95
+ options,
96
+ qrOpenChoiceCheckbox,
97
+ isRepeated
98
+ );
99
+
100
+ if (updatedQrItem) {
101
+ onQrItemChange(updatedQrItem);
102
+ }
103
+
104
+ // If single-selection, uncheck open label
105
+ if (!isRepeated) {
106
+ setOpenLabelChecked(false);
107
+ }
108
+ }
109
+
110
+ function handleOpenLabelChange(openLabelChecked: boolean, changedOpenLabelValue: string) {
111
+ const updatedQrItem = updateOpenLabelAnswer(
112
+ openLabelChecked,
113
+ changedOpenLabelValue,
114
+ answers,
115
+ options,
116
+ qrOpenChoiceCheckbox,
117
+ isRepeated
118
+ );
119
+
120
+ if (updatedQrItem) {
121
+ onQrItemChange(updatedQrItem);
122
+ }
123
+ }
124
+
125
+ function handleOpenLabelCheckedChange(checked: boolean) {
126
+ handleOpenLabelChange(checked, openLabelValue);
127
+ setOpenLabelChecked(checked);
128
+ }
129
+
130
+ function handleOpenLabelInputChange(newValue: string) {
131
+ updateOpenLabelValueWithDebounce(newValue);
132
+ setOpenLabelValue(newValue);
133
+ }
134
+
135
+ // eslint-disable-next-line react-hooks/exhaustive-deps
136
+ const updateOpenLabelValueWithDebounce = useCallback(
137
+ debounce((input: string) => {
138
+ handleOpenLabelChange(openLabelChecked, input);
139
+ }, DEBOUNCE_DURATION),
140
+ [handleOpenLabelChange]
141
+ ); // Dependencies are tested, debounce is causing eslint to not recognise dependencies
142
+
143
+ if (showMinimalView) {
144
+ return (
145
+ <>
146
+ <OpenChoiceCheckboxAnswerValueSetFields
147
+ qItem={qItem}
148
+ options={options}
149
+ answers={answers}
150
+ openLabelText={openLabelText}
151
+ openLabelValue={openLabelValue}
152
+ openLabelChecked={openLabelChecked}
153
+ readOnly={readOnly}
154
+ terminologyError={terminologyError}
155
+ onOptionChange={handleOptionChange}
156
+ onOpenLabelCheckedChange={handleOpenLabelCheckedChange}
157
+ onOpenLabelInputChange={handleOpenLabelInputChange}
158
+ />
159
+ <DisplayInstructions displayInstructions={displayInstructions} readOnly={readOnly} />
160
+ </>
161
+ );
162
+ }
163
+
164
+ return (
165
+ <FullWidthFormComponentBox
166
+ data-test="q-item-open-choice-checkbox-answer-option-box"
167
+ data-linkid={qItem.linkId}
168
+ onClick={() => onFocusLinkId(qItem.linkId)}>
169
+ <ItemFieldGrid qItem={qItem} readOnly={readOnly}>
170
+ <OpenChoiceCheckboxAnswerValueSetFields
171
+ qItem={qItem}
172
+ options={options}
173
+ answers={answers}
174
+ openLabelText={openLabelText}
175
+ openLabelValue={openLabelValue}
176
+ openLabelChecked={openLabelChecked}
177
+ readOnly={readOnly}
178
+ terminologyError={terminologyError}
179
+ onOptionChange={handleOptionChange}
180
+ onOpenLabelCheckedChange={handleOpenLabelCheckedChange}
181
+ onOpenLabelInputChange={handleOpenLabelInputChange}
182
+ />
183
+ </ItemFieldGrid>
184
+ </FullWidthFormComponentBox>
185
+ );
186
+ }
187
+
188
+ export default OpenChoiceCheckboxAnswerValueSetItem;
@@ -31,6 +31,8 @@ import type {
31
31
  PropsWithQrItemChangeHandler,
32
32
  PropsWithShowMinimalViewAttribute
33
33
  } from '../../../interfaces/renderProps.interface';
34
+ import OpenChoiceCheckboxAnswerValueSetItem from './OpenChoiceCheckboxAnswerValueSetItem';
35
+ import OpenChoiceRadioAnswerValueSetItem from './OpenChoiceRadioAnswerValueSetItem';
34
36
 
35
37
  interface OpenChoiceItemSwitcherProps
36
38
  extends PropsWithQrItemChangeHandler,
@@ -48,26 +50,51 @@ function OpenChoiceItemSwitcher(props: OpenChoiceItemSwitcherProps) {
48
50
 
49
51
  switch (getOpenChoiceControlType(qItem)) {
50
52
  case OpenChoiceItemControl.Checkbox:
51
- return (
52
- <OpenChoiceCheckboxAnswerOptionItem
53
- qItem={qItem}
54
- qrItem={qrItem}
55
- isRepeated={qItem['repeats'] ?? false}
56
- showMinimalView={showMinimalView}
57
- parentIsReadOnly={parentIsReadOnly}
58
- onQrItemChange={onQrItemChange}
59
- />
60
- );
53
+ if (qItem.answerValueSet) {
54
+ return (
55
+ <OpenChoiceCheckboxAnswerValueSetItem
56
+ qItem={qItem}
57
+ qrItem={qrItem}
58
+ isRepeated={qItem['repeats'] ?? false}
59
+ showMinimalView={showMinimalView}
60
+ parentIsReadOnly={parentIsReadOnly}
61
+ onQrItemChange={onQrItemChange}
62
+ />
63
+ );
64
+ } else {
65
+ return (
66
+ <OpenChoiceCheckboxAnswerOptionItem
67
+ qItem={qItem}
68
+ qrItem={qrItem}
69
+ isRepeated={qItem['repeats'] ?? false}
70
+ showMinimalView={showMinimalView}
71
+ parentIsReadOnly={parentIsReadOnly}
72
+ onQrItemChange={onQrItemChange}
73
+ />
74
+ );
75
+ }
61
76
  case OpenChoiceItemControl.Radio:
62
- return (
63
- <OpenChoiceRadioAnswerOptionItem
64
- qItem={qItem}
65
- qrItem={qrItem}
66
- isRepeated={qItem['repeats'] ?? false}
67
- parentIsReadOnly={parentIsReadOnly}
68
- onQrItemChange={onQrItemChange}
69
- />
70
- );
77
+ if (qItem.answerValueSet) {
78
+ return (
79
+ <OpenChoiceRadioAnswerValueSetItem
80
+ qItem={qItem}
81
+ qrItem={qrItem}
82
+ isRepeated={qItem['repeats'] ?? false}
83
+ parentIsReadOnly={parentIsReadOnly}
84
+ onQrItemChange={onQrItemChange}
85
+ />
86
+ );
87
+ } else {
88
+ return (
89
+ <OpenChoiceRadioAnswerOptionItem
90
+ qItem={qItem}
91
+ qrItem={qrItem}
92
+ isRepeated={qItem['repeats'] ?? false}
93
+ parentIsReadOnly={parentIsReadOnly}
94
+ onQrItemChange={onQrItemChange}
95
+ />
96
+ );
97
+ }
71
98
  case OpenChoiceItemControl.Autocomplete:
72
99
  return (
73
100
  <OpenChoiceAutocompleteItem
@@ -18,14 +18,15 @@
18
18
  import type { ChangeEvent } from 'react';
19
19
  import React from 'react';
20
20
  import { ChoiceItemOrientation } from '../../../interfaces/choice.enum';
21
- import type { QuestionnaireItem } from 'fhir/r4';
21
+ import type { QuestionnaireItem, QuestionnaireItemAnswerOption } from 'fhir/r4';
22
22
  import { StyledRadioGroup } from '../Item.styles';
23
23
  import RadioButtonWithOpenLabel from '../ItemParts/RadioButtonWithOpenLabel';
24
- import RadioAnswerOptionButtons from '../ItemParts/RadioAnswerOptionButtons';
24
+ import RadioOptionList from '../ItemParts/RadioOptionList';
25
25
  import { getChoiceOrientation } from '../../../utils/choice';
26
26
 
27
27
  interface OpenChoiceRadioAnswerOptionFieldsProps {
28
28
  qItem: QuestionnaireItem;
29
+ options: QuestionnaireItemAnswerOption[];
29
30
  valueRadio: string | null;
30
31
  openLabelText: string | null;
31
32
  openLabelValue: string | null;
@@ -37,6 +38,7 @@ interface OpenChoiceRadioAnswerOptionFieldsProps {
37
38
  function OpenChoiceRadioAnswerOptionFields(props: OpenChoiceRadioAnswerOptionFieldsProps) {
38
39
  const {
39
40
  qItem,
41
+ options,
40
42
  valueRadio,
41
43
  openLabelText,
42
44
  openLabelValue,
@@ -55,7 +57,7 @@ function OpenChoiceRadioAnswerOptionFields(props: OpenChoiceRadioAnswerOptionFie
55
57
  onChange={(e: ChangeEvent<HTMLInputElement>) => onValueChange(e.target.value, null)}
56
58
  value={valueRadio}
57
59
  data-test="q-item-radio-group">
58
- <RadioAnswerOptionButtons qItem={qItem} readOnly={readOnly} />
60
+ <RadioOptionList options={options} readOnly={readOnly} />
59
61
 
60
62
  {openLabelText ? (
61
63
  <RadioButtonWithOpenLabel