@aehrc/smart-forms-renderer 0.44.0 → 1.0.0-alpha.1

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 (86) hide show
  1. package/lib/components/FormComponents/CustomComponent.d.ts +9 -0
  2. package/lib/components/FormComponents/CustomComponent.js +31 -0
  3. package/lib/components/FormComponents/CustomComponent.js.map +1 -0
  4. package/lib/components/FormComponents/CustomComponentWrapper.d.ts +1 -0
  5. package/lib/components/FormComponents/CustomComponentWrapper.js +40 -0
  6. package/lib/components/FormComponents/CustomComponentWrapper.js.map +1 -0
  7. package/lib/components/FormComponents/ItemParts/ItemLabelText.js +4 -1
  8. package/lib/components/FormComponents/ItemParts/ItemLabelText.js.map +1 -1
  9. package/lib/components/FormComponents/ItemParts/ItemLabelWrapper.js +6 -5
  10. package/lib/components/FormComponents/ItemParts/ItemLabelWrapper.js.map +1 -1
  11. package/lib/components/FormComponents/ItemParts/index.d.ts +1 -0
  12. package/lib/components/FormComponents/ItemParts/index.js +18 -0
  13. package/lib/components/FormComponents/ItemParts/index.js.map +1 -0
  14. package/lib/components/FormComponents/OpenChoiceItems/OpenChoiceCheckboxAnswerValueSetItem.js +1 -1
  15. package/lib/components/FormComponents/OpenChoiceItems/OpenChoiceCheckboxAnswerValueSetItem.js.map +1 -1
  16. package/lib/components/FormComponents/SingleItem/SingleItemSwitcher.js +9 -0
  17. package/lib/components/FormComponents/SingleItem/SingleItemSwitcher.js.map +1 -1
  18. package/lib/components/FormComponents/StringItem/index.d.ts +1 -0
  19. package/lib/components/FormComponents/StringItem/index.js +18 -0
  20. package/lib/components/FormComponents/StringItem/index.js.map +1 -0
  21. package/lib/components/FormComponents/index.d.ts +2 -0
  22. package/lib/components/FormComponents/index.js +3 -0
  23. package/lib/components/FormComponents/index.js.map +1 -1
  24. package/lib/components/index.d.ts +2 -1
  25. package/lib/components/index.js +3 -1
  26. package/lib/components/index.js.map +1 -1
  27. package/lib/hooks/index.d.ts +4 -0
  28. package/lib/hooks/index.js +5 -0
  29. package/lib/hooks/index.js.map +1 -1
  30. package/lib/hooks/useBuildForm.d.ts +5 -1
  31. package/lib/hooks/useBuildForm.js +10 -2
  32. package/lib/hooks/useBuildForm.js.map +1 -1
  33. package/lib/index.d.ts +4 -4
  34. package/lib/index.js +3 -3
  35. package/lib/index.js.map +1 -1
  36. package/lib/interfaces/customComponent.d.ts +9 -0
  37. package/lib/interfaces/customComponent.interface.d.ts +9 -0
  38. package/lib/interfaces/customComponent.interface.js +18 -0
  39. package/lib/interfaces/customComponent.interface.js.map +1 -0
  40. package/lib/interfaces/customComponent.js +18 -0
  41. package/lib/interfaces/customComponent.js.map +1 -0
  42. package/lib/interfaces/index.d.ts +1 -0
  43. package/lib/stores/questionnaireStore.d.ts +6 -2
  44. package/lib/stores/questionnaireStore.js +5 -2
  45. package/lib/stores/questionnaireStore.js.map +1 -1
  46. package/lib/stores/rendererStylingStore.d.ts +22 -0
  47. package/lib/stores/rendererStylingStore.js +34 -0
  48. package/lib/stores/rendererStylingStore.js.map +1 -0
  49. package/lib/stores/stylingStore.d.ts +23 -0
  50. package/lib/stores/stylingStore.js +27 -0
  51. package/lib/stores/stylingStore.js.map +1 -0
  52. package/lib/utils/customComponent.d.ts +9 -0
  53. package/lib/utils/customComponent.js +18 -0
  54. package/lib/utils/customComponent.js.map +1 -0
  55. package/lib/utils/enableWhen.js +12 -5
  56. package/lib/utils/enableWhen.js.map +1 -1
  57. package/lib/utils/index.d.ts +1 -0
  58. package/lib/utils/index.js +1 -0
  59. package/lib/utils/index.js.map +1 -1
  60. package/lib/utils/manageForm.d.ts +3 -1
  61. package/lib/utils/manageForm.js +3 -2
  62. package/lib/utils/manageForm.js.map +1 -1
  63. package/lib/utils/openChoice.js +20 -2
  64. package/lib/utils/openChoice.js.map +1 -1
  65. package/package.json +3 -1
  66. package/src/components/FormComponents/ItemParts/ItemLabelText.tsx +9 -1
  67. package/src/components/FormComponents/ItemParts/ItemLabelWrapper.tsx +12 -10
  68. package/src/components/FormComponents/ItemParts/index.ts +18 -0
  69. package/src/components/FormComponents/OpenChoiceItems/OpenChoiceCheckboxAnswerValueSetItem.tsx +1 -1
  70. package/src/components/FormComponents/SingleItem/SingleItemSwitcher.tsx +20 -0
  71. package/src/components/FormComponents/StringItem/index.ts +18 -0
  72. package/src/components/FormComponents/index.ts +4 -0
  73. package/src/components/index.ts +6 -1
  74. package/src/hooks/index.ts +6 -0
  75. package/src/hooks/useBuildForm.ts +16 -3
  76. package/src/index.ts +22 -3
  77. package/src/interfaces/customComponent.interface.ts +27 -0
  78. package/src/interfaces/index.ts +1 -0
  79. package/src/stores/questionnaireStore.ts +11 -3
  80. package/src/stores/rendererStylingStore.ts +67 -0
  81. package/src/stories/assets/questionnaires/QBehaviorOther.ts +92 -0
  82. package/src/stories/sdc/BehaviorOther.stories.tsx +7 -0
  83. package/src/utils/enableWhen.ts +15 -5
  84. package/src/utils/index.ts +2 -0
  85. package/src/utils/manageForm.ts +12 -2
  86. package/src/utils/openChoice.ts +10 -4
@@ -21,3 +21,7 @@ export { RepeatGroup } from './RepeatGroup';
21
21
  export { GroupTable } from './Tables';
22
22
  export { GridGroup } from './GridGroup';
23
23
  export { parseFhirDateToDisplayDate } from './DateTimeItems';
24
+ export { ItemFieldGrid } from './ItemParts';
25
+
26
+ // item type components
27
+ export { StringField } from './StringItem';
@@ -23,5 +23,10 @@ export {
23
23
  RepeatGroup,
24
24
  GroupTable,
25
25
  GridGroup,
26
- parseFhirDateToDisplayDate
26
+ parseFhirDateToDisplayDate,
27
+ ItemFieldGrid,
28
+ StringField
27
29
  } from './FormComponents';
30
+
31
+ // Styled MUI components
32
+ export { FullWidthFormComponentBox } from './Box.styles';
@@ -1,3 +1,9 @@
1
1
  export { default as useHidden } from './useHidden';
2
+ export { default as useReadOnly } from './useReadOnly';
2
3
  export { default as useBuildForm } from './useBuildForm';
3
4
  export { default as useRendererQueryClient } from './useRendererQueryClient';
5
+ export { default as useRenderingExtensions } from './useRenderingExtensions';
6
+ export { default as useValidationFeedback } from './useValidationFeedback';
7
+
8
+ // CalculatedExpression hooks
9
+ export { default as useStringCalculatedExpression } from './useStringCalculatedExpression';
@@ -15,9 +15,10 @@
15
15
  * limitations under the License.
16
16
  */
17
17
 
18
- import { useLayoutEffect, useState } from 'react';
18
+ import { ComponentType, useLayoutEffect, useState } from 'react';
19
19
  import { buildForm } from '../utils';
20
20
  import type { Questionnaire, QuestionnaireResponse } from 'fhir/r4';
21
+ import { RendererStyling, useRendererStylingStore } from '../stores/rendererStylingStore';
21
22
 
22
23
  /**
23
24
  * React hook wrapping around the buildForm() function to build a form from a questionnaire and an optional QuestionnaireResponse.
@@ -28,6 +29,8 @@ import type { Questionnaire, QuestionnaireResponse } from 'fhir/r4';
28
29
  * @param readOnly - Applies read-only mode to all items in the form view
29
30
  * @param terminologyServerUrl - Terminology server url to fetch terminology. If not provided, the default terminology server will be used. (optional)
30
31
  * @param additionalVariables - Additional key-value pair of SDC variables `Record<name, variable extension>` for testing (optional)
32
+ * @param rendererStyling - Renderer styling to be applied to the form. See docs for styling options. (optional)
33
+ * @param customComponents - FIXME add comment
31
34
  *
32
35
  * @author Sean Fong
33
36
  */
@@ -36,17 +39,27 @@ function useBuildForm(
36
39
  questionnaireResponse?: QuestionnaireResponse,
37
40
  readOnly?: boolean,
38
41
  terminologyServerUrl?: string,
39
- additionalVariables?: Record<string, object>
42
+ additionalVariables?: Record<string, object>,
43
+ rendererStyling?: RendererStyling,
44
+ customComponents?: Record<string, ComponentType<any>>
40
45
  ) {
41
46
  const [isBuilding, setIsBuilding] = useState(true);
42
47
 
48
+ const setRendererStyling = useRendererStylingStore.use.setRendererStyling();
49
+
43
50
  useLayoutEffect(() => {
51
+ // Set optional renderer styling
52
+ if (rendererStyling) {
53
+ setRendererStyling(rendererStyling);
54
+ }
55
+
44
56
  buildForm(
45
57
  questionnaire,
46
58
  questionnaireResponse,
47
59
  readOnly,
48
60
  terminologyServerUrl,
49
- additionalVariables
61
+ additionalVariables,
62
+ customComponents
50
63
  ).then(() => {
51
64
  setIsBuilding(false);
52
65
  });
package/src/index.ts CHANGED
@@ -1,5 +1,12 @@
1
1
  // interface exports
2
- export type { Tab, Tabs, Variables, VariableXFhirQuery, LaunchContext } from './interfaces';
2
+ export type {
3
+ Tab,
4
+ Tabs,
5
+ Variables,
6
+ VariableXFhirQuery,
7
+ LaunchContext,
8
+ CustomComponentProps
9
+ } from './interfaces';
3
10
 
4
11
  // component exports
5
12
  export type { SmartFormsRendererProps } from './components';
@@ -11,7 +18,10 @@ export {
11
18
  RepeatGroup,
12
19
  GroupTable,
13
20
  GridGroup,
14
- parseFhirDateToDisplayDate
21
+ parseFhirDateToDisplayDate,
22
+ ItemFieldGrid,
23
+ StringField,
24
+ FullWidthFormComponentBox
15
25
  } from './components';
16
26
 
17
27
  // state management store exports
@@ -33,7 +43,15 @@ export {
33
43
  } from './stores';
34
44
 
35
45
  // hooks exports
36
- export { useHidden, useBuildForm, useRendererQueryClient } from './hooks';
46
+ export {
47
+ useHidden,
48
+ useReadOnly,
49
+ useBuildForm,
50
+ useRendererQueryClient,
51
+ useRenderingExtensions,
52
+ useValidationFeedback,
53
+ useStringCalculatedExpression
54
+ } from './hooks';
37
55
 
38
56
  // utils exports
39
57
  export type { ItemToRepopulate } from './utils';
@@ -43,6 +61,7 @@ export {
43
61
  getResponse,
44
62
  removeEmptyAnswersFromResponse,
45
63
  removeInternalIdsFromResponse,
64
+ createEmptyQrItem,
46
65
  isSpecificItemControl,
47
66
  isRepeatItemAndNotCheckbox,
48
67
  initialiseQuestionnaireResponse,
@@ -0,0 +1,27 @@
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 { QuestionnaireItem, type QuestionnaireResponseItem } from 'fhir/r4';
19
+
20
+ export interface CustomComponentProps {
21
+ qItem: QuestionnaireItem;
22
+ qrItem: QuestionnaireResponseItem | null;
23
+ isRepeated: boolean;
24
+ isTabled: boolean;
25
+ parentIsReadOnly?: boolean;
26
+ onQrItemChange: (qrItem: QuestionnaireResponseItem) => unknown;
27
+ }
@@ -19,3 +19,4 @@ export type { Tab, Tabs } from './tab.interface';
19
19
  export type { Variables, VariableXFhirQuery } from './variables.interface';
20
20
  export type { LaunchContext } from './populate.interface';
21
21
  export type { EnableWhenItems, EnableWhenExpressions } from './enableWhen.interface';
22
+ export type { CustomComponentProps } from './customComponent.interface';
@@ -48,6 +48,8 @@ import { questionnaireResponseStore } from './questionnaireResponseStore';
48
48
  import { createQuestionnaireResponseItemMap } from '../utils/questionnaireResponseStoreUtils/updatableResponseItems';
49
49
  import { insertCompleteAnswerOptionsIntoQuestionnaire } from '../utils/questionnaireStoreUtils/insertAnswerOptions';
50
50
  import type { InitialExpression } from '../interfaces/initialExpression.interface';
51
+ import React from 'react';
52
+ import { CustomComponentProps } from '../interfaces/customComponent.interface';
51
53
 
52
54
  /**
53
55
  * QuestionnaireStore properties and methods
@@ -116,6 +118,7 @@ export interface QuestionnaireStoreType {
116
118
  cachedValueSetCodings: Record<string, Coding[]>;
117
119
  fhirPathContext: Record<string, any>;
118
120
  populatedContext: Record<string, any>;
121
+ customComponents: Record<string, React.ComponentType<CustomComponentProps>>;
119
122
  focusedLinkId: string;
120
123
  readOnly: boolean;
121
124
  buildSourceQuestionnaire: (
@@ -123,7 +126,8 @@ export interface QuestionnaireStoreType {
123
126
  questionnaireResponse?: QuestionnaireResponse,
124
127
  additionalVariables?: Record<string, object>,
125
128
  terminologyServerUrl?: string,
126
- readOnly?: boolean
129
+ readOnly?: boolean,
130
+ customComponents?: Record<string, React.ComponentType<any>>
127
131
  ) => Promise<void>;
128
132
  destroySourceQuestionnaire: () => void;
129
133
  switchTab: (newTabIndex: number) => void;
@@ -182,6 +186,7 @@ export const questionnaireStore = createStore<QuestionnaireStoreType>()((set, ge
182
186
  cachedValueSetCodings: {},
183
187
  fhirPathContext: {},
184
188
  populatedContext: {},
189
+ customComponents: {},
185
190
  focusedLinkId: '',
186
191
  readOnly: false,
187
192
  buildSourceQuestionnaire: async (
@@ -189,7 +194,8 @@ export const questionnaireStore = createStore<QuestionnaireStoreType>()((set, ge
189
194
  questionnaireResponse = structuredClone(emptyResponse),
190
195
  additionalVariables = {},
191
196
  terminologyServerUrl = terminologyServerStore.getState().url,
192
- readOnly = false
197
+ readOnly = false,
198
+ customComponents = {}
193
199
  ) => {
194
200
  const questionnaireModel = await createQuestionnaireModel(
195
201
  questionnaire,
@@ -242,6 +248,7 @@ export const questionnaireStore = createStore<QuestionnaireStoreType>()((set, ge
242
248
  processedValueSetCodings: questionnaireModel.processedValueSetCodings,
243
249
  processedValueSetUrls: questionnaireModel.processedValueSetUrls,
244
250
  fhirPathContext: updatedFhirPathContext,
251
+ customComponents: customComponents,
245
252
  readOnly: readOnly
246
253
  });
247
254
  },
@@ -264,7 +271,8 @@ export const questionnaireStore = createStore<QuestionnaireStoreType>()((set, ge
264
271
  answerExpressions: {},
265
272
  processedValueSetCodings: {},
266
273
  processedValueSetUrls: {},
267
- fhirPathContext: {}
274
+ fhirPathContext: {},
275
+ customComponents: {}
268
276
  }),
269
277
  switchTab: (newTabIndex: number) => set(() => ({ currentTabIndex: newTabIndex })),
270
278
  switchPage: (newPageIndex: number) => set(() => ({ currentPageIndex: newPageIndex })),
@@ -0,0 +1,67 @@
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 { createStore } from 'zustand/vanilla';
19
+ import { createSelectors } from './selector';
20
+
21
+ export interface RendererStyling {
22
+ itemLabelFontWeight?:
23
+ | '100'
24
+ | '200'
25
+ | '300'
26
+ | '400'
27
+ | '500'
28
+ | '600'
29
+ | '700'
30
+ | '800'
31
+ | '900'
32
+ | 'default';
33
+ }
34
+
35
+ /**
36
+ * RendererStylingStore properties and methods
37
+ *
38
+ * @author Sean Fong
39
+ */
40
+ export interface RendererStylingStoreType {
41
+ itemLabelFontWeight:
42
+ | '100'
43
+ | '200'
44
+ | '300'
45
+ | '400'
46
+ | '500'
47
+ | '600'
48
+ | '700'
49
+ | '800'
50
+ | '900'
51
+ | 'default';
52
+ setRendererStyling: (params: RendererStyling) => void;
53
+ }
54
+
55
+ /**
56
+ * @author Sean Fong
57
+ */
58
+ export const rendererStylingStore = createStore<RendererStylingStoreType>()((set) => ({
59
+ itemLabelFontWeight: 'default',
60
+ setRendererStyling: (params: RendererStyling) => {
61
+ set(() => ({
62
+ itemLabelFontWeight: params.itemLabelFontWeight ?? 'default'
63
+ }));
64
+ }
65
+ }));
66
+
67
+ export const useRendererStylingStore = createSelectors(rendererStylingStore);
@@ -315,6 +315,98 @@ export const qEnableWhen: Questionnaire = {
315
315
  ]
316
316
  };
317
317
 
318
+ export const qEnableWhenMultiCheckbox: Questionnaire = {
319
+ resourceType: 'Questionnaire',
320
+ id: 'EnableWhenMultiCheckbox',
321
+ name: 'EnableWhenMultiCheckbox',
322
+ title: 'EnableWhen Multi-select Checkbox',
323
+ version: '0.1.0',
324
+ status: 'draft',
325
+ date: '2024-05-01',
326
+ url: 'https://smartforms.csiro.au/docs/behavior/other/enable-when-multi-checkbox',
327
+ item: [
328
+ {
329
+ extension: [
330
+ {
331
+ url: 'http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl',
332
+ valueCodeableConcept: {
333
+ coding: [
334
+ {
335
+ system: 'http://hl7.org/fhir/questionnaire-item-control',
336
+ code: 'check-box'
337
+ }
338
+ ]
339
+ }
340
+ },
341
+ {
342
+ url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-openLabel',
343
+ valueString: 'Other, please specify'
344
+ }
345
+ ],
346
+ linkId: 'select-conditions-list',
347
+ text: 'Select one or more conditions',
348
+ type: 'open-choice',
349
+ repeats: true,
350
+ answerOption: [
351
+ {
352
+ valueString: 'Condition A (Displays Clinical guidance: Condition A question)'
353
+ },
354
+ {
355
+ valueString: 'Condition B (Displays Clinical guidance: Condition B question)'
356
+ },
357
+ {
358
+ valueString: 'Condition C (Displays Clinical guidance: Condition C question)'
359
+ },
360
+ {
361
+ valueString: 'Condition D'
362
+ },
363
+ {
364
+ valueString: 'Condition E'
365
+ },
366
+ {
367
+ valueString: 'Condition F'
368
+ }
369
+ ]
370
+ },
371
+ {
372
+ linkId: 'clinical-guidance-a',
373
+ text: 'Clinical guidance: Condition A',
374
+ type: 'display',
375
+ enableWhen: [
376
+ {
377
+ question: 'select-conditions-list',
378
+ operator: '=',
379
+ answerString: 'Condition A (Displays Clinical guidance: Condition A question)'
380
+ }
381
+ ]
382
+ },
383
+ {
384
+ linkId: 'clinical-guidance-b',
385
+ text: 'Clinical guidance: Condition B',
386
+ type: 'display',
387
+ enableWhen: [
388
+ {
389
+ question: 'select-conditions-list',
390
+ operator: '=',
391
+ answerString: 'Condition B (Displays Clinical guidance: Condition B question)'
392
+ }
393
+ ]
394
+ },
395
+ {
396
+ linkId: 'clinical-guidance-c',
397
+ text: 'Clinical guidance: Condition C',
398
+ type: 'display',
399
+ enableWhen: [
400
+ {
401
+ question: 'select-conditions-list',
402
+ operator: '=',
403
+ answerString: 'Condition C (Displays Clinical guidance: Condition C question)'
404
+ }
405
+ ]
406
+ }
407
+ ]
408
+ };
409
+
318
410
  export const qEnableBehaviorAll: Questionnaire = {
319
411
  resourceType: 'Questionnaire',
320
412
  id: 'EnableBehaviorAll',
@@ -23,6 +23,7 @@ import {
23
23
  qEnableWhen,
24
24
  qEnableWhenExpressionSimple,
25
25
  qEnableWhenExpressionTabs,
26
+ qEnableWhenMultiCheckbox,
26
27
  qInitialRepeats,
27
28
  qInitialSingle,
28
29
  qText
@@ -59,6 +60,12 @@ export const EnableWhen: Story = {
59
60
  }
60
61
  };
61
62
 
63
+ export const EnableWhenMultiCheckbox: Story = {
64
+ args: {
65
+ questionnaire: qEnableWhenMultiCheckbox
66
+ }
67
+ };
68
+
62
69
  export const EnableBehaviorAll: Story = {
63
70
  args: {
64
71
  questionnaire: qEnableBehaviorAll
@@ -339,19 +339,29 @@ export function checkItemIsEnabledSingle(
339
339
  ): boolean {
340
340
  const checkedIsEnabledItems: boolean[] = [];
341
341
 
342
+ // Check if linked item satisfies enableWhen condition
342
343
  for (const linkedItem of enableWhenItemProperties.linked) {
344
+ let isEnabledForThisLinkedItem = false;
345
+
346
+ // Linked item has answers
343
347
  if (linkedItem.answer && linkedItem.answer.length > 0) {
348
+ // Check if linked answer within item satisfies enableWhen condition
349
+ // Exit early once a linked answer is found to satisfy the condition
344
350
  for (const answer of linkedItem.answer) {
345
- const isEnabledForThisLinkedItem = isEnabledAnswerTypeSwitcher(
351
+ const isEnabledForThisLinkedAnswer = isEnabledAnswerTypeSwitcher(
346
352
  linkedItem.enableWhen,
347
353
  answer
348
354
  );
349
355
 
350
- // In a repeat item, if at least one answer satisfies the condition, the item is enabled
351
- // FIXME need to look further at this
352
- checkedIsEnabledItems.push(isEnabledForThisLinkedItem);
353
- break;
356
+ if (isEnabledForThisLinkedAnswer) {
357
+ isEnabledForThisLinkedItem = true;
358
+ break;
359
+ }
354
360
  }
361
+
362
+ // Push result of the linked item to the checkedIsEnabledItems array
363
+ checkedIsEnabledItems.push(isEnabledForThisLinkedItem);
364
+
355
365
  continue;
356
366
  }
357
367
 
@@ -23,6 +23,8 @@ export {
23
23
  removeInternalIdsFromResponse
24
24
  } from './manageForm';
25
25
  export { initialiseQuestionnaireResponse } from './initialise';
26
+ export { createEmptyQrItem } from './qrItem';
27
+
26
28
  export { isSpecificItemControl } from './itemControl';
27
29
  export { isRepeatItemAndNotCheckbox } from './qItem';
28
30
  export type { ItemToRepopulate } from './repopulateItems';
@@ -11,6 +11,7 @@ import { readEncounter, readPatient, readUser } from '../api/smartClient';
11
11
  import type Client from 'fhirclient/lib/Client';
12
12
  import { updateQuestionnaireResponse } from './genericRecursive';
13
13
  import { removeInternalRepeatIdsRecursive } from './removeRepeatId';
14
+ import { ComponentType } from 'react';
14
15
 
15
16
  /**
16
17
  * Build the form with an initial Questionnaire and an optional filled QuestionnaireResponse.
@@ -22,6 +23,7 @@ import { removeInternalRepeatIdsRecursive } from './removeRepeatId';
22
23
  * @param readOnly - Applies read-only mode to all items in the form view
23
24
  * @param terminologyServerUrl - Terminology server url to fetch terminology. If not provided, the default terminology server will be used. (optional)
24
25
  * @param additionalVariables - Additional key-value pair of SDC variables `Record<name, variable extension>` for testing (optional)
26
+ * @param customComponents - FIXME add comment
25
27
  *
26
28
  * @author Sean Fong
27
29
  */
@@ -30,7 +32,8 @@ export async function buildForm(
30
32
  questionnaireResponse?: QuestionnaireResponse,
31
33
  readOnly?: boolean,
32
34
  terminologyServerUrl?: string,
33
- additionalVariables?: Record<string, object>
35
+ additionalVariables?: Record<string, object>,
36
+ customComponents?: Record<string, ComponentType<any>>
34
37
  ): Promise<void> {
35
38
  // Reset terminology server
36
39
  if (terminologyServerUrl) {
@@ -42,7 +45,14 @@ export async function buildForm(
42
45
  // QR is set to undefined here to prevent it from being initialised twice. This is defined like that for backward compatibility purposes.
43
46
  await questionnaireStore
44
47
  .getState()
45
- .buildSourceQuestionnaire(questionnaire, undefined, additionalVariables, terminologyServerUrl);
48
+ .buildSourceQuestionnaire(
49
+ questionnaire,
50
+ undefined,
51
+ additionalVariables,
52
+ terminologyServerUrl,
53
+ undefined,
54
+ customComponents
55
+ );
46
56
 
47
57
  const initialisedQuestionnaireResponse = initialiseQuestionnaireResponse(
48
58
  questionnaire,
@@ -24,6 +24,7 @@ import type {
24
24
  import { OpenChoiceItemControl } from '../interfaces/choice.enum';
25
25
  import { isSpecificItemControl } from './itemControl';
26
26
  import isEqual from 'lodash.isequal';
27
+ import differenceWith from 'lodash.differencewith';
27
28
 
28
29
  /**
29
30
  * Update open choice answer based on open label value
@@ -121,10 +122,15 @@ export function getOldOpenLabelAnswer(
121
122
  answers: QuestionnaireResponseItemAnswer[],
122
123
  options: QuestionnaireItemAnswerOption[]
123
124
  ): QuestionnaireResponseItemAnswer | null {
124
- const openLabelAnswer = answers.find(
125
- (answer) => !options.some((option) => isEqual(option, answer))
126
- );
127
- return openLabelAnswer ?? null;
125
+ const answersWithoutId = answers.map((answer) => {
126
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
127
+ const { id, ...rest } = answer;
128
+ return rest as QuestionnaireResponseItemAnswer;
129
+ });
130
+
131
+ const outliers = differenceWith(answersWithoutId, options, isEqual);
132
+
133
+ return outliers?.[0] ?? null;
128
134
  }
129
135
 
130
136
  /**