diff --git a/packages/smart-forms-renderer/src/components/FormComponents/GroupItem/GroupItemView.tsx b/packages/smart-forms-renderer/src/components/FormComponents/GroupItem/GroupItemView.tsx
index 88ec8b5b..619e34da 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/GroupItem/GroupItemView.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/GroupItem/GroupItemView.tsx
@@ -80,7 +80,7 @@ function GroupItemView(props: GroupItemViewProps) {
onQrRepeatGroupChange
} = props;
- const readOnly = useReadOnly(qItem, parentIsReadOnly);
+ const readOnly = useReadOnly(qItem, parentIsReadOnly, parentRepeatGroupIndex);
// Render collapsible group item
// If group item is a repeating instance, do not render group item as collapsible
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/ItemParts/FlyoverItem.tsx b/packages/smart-forms-renderer/src/components/FormComponents/ItemParts/FlyoverItem.tsx
new file mode 100644
index 00000000..cc42a2d6
--- /dev/null
+++ b/packages/smart-forms-renderer/src/components/FormComponents/ItemParts/FlyoverItem.tsx
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2024 Commonwealth Scientific and Industrial Research
+ * Organisation (CSIRO) ABN 41 687 119 230.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React from 'react';
+import Tooltip from '@mui/material/Tooltip';
+import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
+import { useQuestionnaireStore } from '../../../stores';
+
+interface FlyoverItemProps {
+ displayFlyover: string;
+}
+
+function FlyoverItem(props: FlyoverItemProps) {
+ const { displayFlyover } = props;
+
+ const sdcUiOverrideComponents = useQuestionnaireStore.use.sdcUiOverrideComponents();
+ const FlyoverOverrideComponent = sdcUiOverrideComponents['flyover'];
+
+ // If a flyover override component is defined for this item, render it
+ if (FlyoverOverrideComponent && typeof FlyoverOverrideComponent === 'function') {
+ return ;
+ }
+
+ return (
+
+
+
+
+
+ );
+}
+
+export default FlyoverItem;
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/ItemParts/ItemLabelWrapper.tsx b/packages/smart-forms-renderer/src/components/FormComponents/ItemParts/ItemLabelWrapper.tsx
index 72a9a71c..afc7ecdc 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/ItemParts/ItemLabelWrapper.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/ItemParts/ItemLabelWrapper.tsx
@@ -21,10 +21,9 @@ import ContextDisplayItem from './ContextDisplayItem';
import type { QuestionnaireItem } from 'fhir/r4';
import { getContextDisplays } from '../../../utils/tabs';
import ItemLabelText from './ItemLabelText';
-import Tooltip from '@mui/material/Tooltip';
import Typography from '@mui/material/Typography';
import useRenderingExtensions from '../../../hooks/useRenderingExtensions';
-import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
+import FlyoverItem from './FlyoverItem';
interface LabelWrapperProps {
qItem: QuestionnaireItem;
@@ -39,40 +38,20 @@ function ItemLabelWrapper(props: LabelWrapperProps) {
return (
-
-
-
- {required ? (
-
- *
-
- ) : null}
-
-
- {displayFlyover !== '' ? (
-
- ) : null}
-
-
-
-
+
+ {required ? (
+
+ *
+
+ ) : null}
+
+
+ {displayFlyover !== '' ? : null}
+
+
{contextDisplayItems.map((item) => {
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/RepeatGroup/RepeatGroupItem.tsx b/packages/smart-forms-renderer/src/components/FormComponents/RepeatGroup/RepeatGroupItem.tsx
index 6aa5afdc..b24aa8fb 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/RepeatGroup/RepeatGroupItem.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/RepeatGroup/RepeatGroupItem.tsx
@@ -55,7 +55,7 @@ function RepeatGroupItem(props: RepeatGroupItemProps) {
onQrItemChange
} = props;
- const readOnly = useReadOnly(qItem, parentIsReadOnly);
+ const readOnly = useReadOnly(qItem, parentIsReadOnly, repeatGroupIndex);
return (
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/SingleItem/SingleItem.tsx b/packages/smart-forms-renderer/src/components/FormComponents/SingleItem/SingleItem.tsx
index de9f395a..819d7a7e 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/SingleItem/SingleItem.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/SingleItem/SingleItem.tsx
@@ -102,7 +102,7 @@ function SingleItem(props: SingleItemProps) {
[qItem]
);
- const readOnly = useReadOnly(qItem, parentIsReadOnly);
+ const readOnly = useReadOnly(qItem, parentIsReadOnly, parentRepeatGroupIndex);
const itemIsHidden = useHidden(qItem, parentRepeatGroupIndex);
return (
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/SingleItem/SingleItemSwitcher.tsx b/packages/smart-forms-renderer/src/components/FormComponents/SingleItem/SingleItemSwitcher.tsx
index 314f60ed..ad081d20 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/SingleItem/SingleItemSwitcher.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/SingleItem/SingleItemSwitcher.tsx
@@ -57,14 +57,14 @@ function SingleItemSwitcher(props: SingleItemSwitcherProps) {
const { qItem, qrItem, isRepeated, isTabled, showMinimalView, parentIsReadOnly, onQrItemChange } =
props;
- const customComponents = useQuestionnaireStore.use.customComponents();
- const CustomComponent = customComponents[qItem.linkId];
+ const qItemOverrideComponents = useQuestionnaireStore.use.qItemOverrideComponents();
+ const QItemOverrideComponent = qItemOverrideComponents[qItem.linkId];
- // If a custom component is defined for this item, render it
+ // If a qItem override component is defined for this item, render it
// Don't get too strict with the checks for now
- if (CustomComponent && typeof CustomComponent === 'function') {
+ if (QItemOverrideComponent && typeof QItemOverrideComponent === 'function') {
return (
- ;
+ return ;
case 'boolean':
return (
);
case 'quantity':
- // FIXME quantity item uses the same component as decimal item currently
return (
` for testing (optional)
* @param rendererStylingOptions - Renderer styling to be applied to the form. See docs for styling options. (optional)
- * @param customComponents - FIXME add comment
+ * @param qItemOverrideComponents - FIXME add comment
+ * @param sdcUiOverrideComponents - FIXME add comment
+ *
*
* @author Sean Fong
*/
@@ -43,7 +46,8 @@ function useBuildForm(
terminologyServerUrl?: string,
additionalVariables?: Record,
rendererStylingOptions?: RendererStyling,
- customComponents?: Record>
+ qItemOverrideComponents?: Record>,
+ sdcUiOverrideComponents?: Record>
) {
const [isBuilding, setIsBuilding] = useState(true);
@@ -61,19 +65,21 @@ function useBuildForm(
readOnly,
terminologyServerUrl,
additionalVariables,
- customComponents
+ qItemOverrideComponents,
+ sdcUiOverrideComponents
).then(() => {
setIsBuilding(false);
});
}, [
- customComponents,
questionnaire,
questionnaireResponse,
readOnly,
- rendererStylingOptions,
- setRendererStyling,
terminologyServerUrl,
- additionalVariables
+ additionalVariables,
+ rendererStylingOptions,
+ qItemOverrideComponents,
+ sdcUiOverrideComponents,
+ setRendererStyling
]);
return isBuilding;
diff --git a/packages/smart-forms-renderer/src/hooks/useHidden.ts b/packages/smart-forms-renderer/src/hooks/useHidden.ts
index 0030987b..2d27076b 100644
--- a/packages/smart-forms-renderer/src/hooks/useHidden.ts
+++ b/packages/smart-forms-renderer/src/hooks/useHidden.ts
@@ -19,6 +19,7 @@ import type { QuestionnaireItem } from 'fhir/r4';
import { useQuestionnaireStore } from '../stores';
import { isHiddenByEnableWhen } from '../utils/qItem';
import { structuredDataCapture } from 'fhir-sdc-helpers';
+import { useRendererStylingStore } from '../stores/rendererStylingStore';
/**
* React hook to determine if a QuestionnaireItem is hidden via item.hidden, enableWhens, enableWhenExpressions.
@@ -31,10 +32,21 @@ function useHidden(qItem: QuestionnaireItem, parentRepeatGroupIndex?: number): b
const enableWhenItems = useQuestionnaireStore.use.enableWhenItems();
const enableWhenExpressions = useQuestionnaireStore.use.enableWhenExpressions();
+ const enableWhenAsReadOnly = useRendererStylingStore.use.enableWhenAsReadOnly();
+
if (structuredDataCapture.getHidden(qItem)) {
return true;
}
+ // If enableWhenAsReadOnly is true, then items hidden by enableWhen should be displayed, but set as readOnly
+ // If enableWhenAsReadOnly is 'non-group', then items hidden by enableWhen should be displayed, but set as readOnly - only applies if item.type != group
+ if (
+ enableWhenAsReadOnly === true ||
+ (enableWhenAsReadOnly === 'non-group' && qItem.type !== 'group')
+ ) {
+ return false;
+ }
+
return isHiddenByEnableWhen({
linkId: qItem.linkId,
enableWhenIsActivated,
diff --git a/packages/smart-forms-renderer/src/hooks/useReadOnly.ts b/packages/smart-forms-renderer/src/hooks/useReadOnly.ts
index e623d826..5e206fd7 100644
--- a/packages/smart-forms-renderer/src/hooks/useReadOnly.ts
+++ b/packages/smart-forms-renderer/src/hooks/useReadOnly.ts
@@ -17,9 +17,40 @@
import type { QuestionnaireItem } from 'fhir/r4';
import useRenderingExtensions from './useRenderingExtensions';
+import { useRendererStylingStore } from '../stores/rendererStylingStore';
+import { isHiddenByEnableWhen } from '../utils/qItem';
+import { useQuestionnaireStore } from '../stores';
-function useReadOnly(qItem: QuestionnaireItem, parentIsReadOnly: boolean | undefined): boolean {
+function useReadOnly(
+ qItem: QuestionnaireItem,
+ parentIsReadOnly: boolean | undefined,
+ parentRepeatGroupIndex?: number
+): boolean {
let { readOnly } = useRenderingExtensions(qItem);
+
+ const enableWhenIsActivated = useQuestionnaireStore.use.enableWhenIsActivated();
+ const enableWhenItems = useQuestionnaireStore.use.enableWhenItems();
+ const enableWhenExpressions = useQuestionnaireStore.use.enableWhenExpressions();
+
+ const enableWhenAsReadOnly = useRendererStylingStore.use.enableWhenAsReadOnly();
+
+ // If enableWhenAsReadOnly is true, then items hidden by enableWhen should be displayed, but set as readOnly
+ // If enableWhenAsReadOnly is 'non-group', then items hidden by enableWhen should be displayed, but set as readOnly - only applies if item.type != group
+ if (!readOnly) {
+ if (
+ enableWhenAsReadOnly === true ||
+ (enableWhenAsReadOnly === 'non-group' && qItem.type !== 'group')
+ ) {
+ readOnly = isHiddenByEnableWhen({
+ linkId: qItem.linkId,
+ enableWhenIsActivated,
+ enableWhenItems,
+ enableWhenExpressions,
+ parentRepeatGroupIndex
+ });
+ }
+ }
+
if (typeof parentIsReadOnly === 'boolean' && parentIsReadOnly) {
readOnly = parentIsReadOnly;
}
diff --git a/packages/smart-forms-renderer/src/index.ts b/packages/smart-forms-renderer/src/index.ts
index 9a46273f..2caf276a 100644
--- a/packages/smart-forms-renderer/src/index.ts
+++ b/packages/smart-forms-renderer/src/index.ts
@@ -5,7 +5,8 @@ export type {
Variables,
VariableXFhirQuery,
LaunchContext,
- CustomComponentProps
+ QItemOverrideComponentProps,
+ SdcUiOverrideComponentProps
} from './interfaces';
// component exports
diff --git a/packages/smart-forms-renderer/src/interfaces/index.ts b/packages/smart-forms-renderer/src/interfaces/index.ts
index 1c1bf7e6..ed7b7218 100644
--- a/packages/smart-forms-renderer/src/interfaces/index.ts
+++ b/packages/smart-forms-renderer/src/interfaces/index.ts
@@ -19,4 +19,7 @@ export type { Tab, Tabs } from './tab.interface';
export type { Variables, VariableXFhirQuery } from './variables.interface';
export type { LaunchContext } from './populate.interface';
export type { EnableWhenItems, EnableWhenExpressions } from './enableWhen.interface';
-export type { CustomComponentProps } from './customComponent.interface';
+export type {
+ QItemOverrideComponentProps,
+ SdcUiOverrideComponentProps
+} from './overrideComponent.interface';
diff --git a/packages/smart-forms-renderer/src/interfaces/customComponent.interface.ts b/packages/smart-forms-renderer/src/interfaces/overrideComponent.interface.ts
similarity index 83%
rename from packages/smart-forms-renderer/src/interfaces/customComponent.interface.ts
rename to packages/smart-forms-renderer/src/interfaces/overrideComponent.interface.ts
index e0d29eb0..0846ba63 100644
--- a/packages/smart-forms-renderer/src/interfaces/customComponent.interface.ts
+++ b/packages/smart-forms-renderer/src/interfaces/overrideComponent.interface.ts
@@ -15,9 +15,9 @@
* limitations under the License.
*/
-import { QuestionnaireItem, type QuestionnaireResponseItem } from 'fhir/r4';
+import type { QuestionnaireItem, QuestionnaireResponseItem } from 'fhir/r4';
-export interface CustomComponentProps {
+export interface QItemOverrideComponentProps {
qItem: QuestionnaireItem;
qrItem: QuestionnaireResponseItem | null;
isRepeated: boolean;
@@ -25,3 +25,7 @@ export interface CustomComponentProps {
parentIsReadOnly?: boolean;
onQrItemChange: (qrItem: QuestionnaireResponseItem) => unknown;
}
+
+export interface SdcUiOverrideComponentProps {
+ displayText: string;
+}
diff --git a/packages/smart-forms-renderer/src/stores/questionnaireStore.ts b/packages/smart-forms-renderer/src/stores/questionnaireStore.ts
index 93f00e5a..a6fb11ea 100644
--- a/packages/smart-forms-renderer/src/stores/questionnaireStore.ts
+++ b/packages/smart-forms-renderer/src/stores/questionnaireStore.ts
@@ -48,7 +48,7 @@ import { questionnaireResponseStore } from './questionnaireResponseStore';
import { createQuestionnaireResponseItemMap } from '../utils/questionnaireResponseStoreUtils/updatableResponseItems';
import { insertCompleteAnswerOptionsIntoQuestionnaire } from '../utils/questionnaireStoreUtils/insertAnswerOptions';
import type { InitialExpression } from '../interfaces/initialExpression.interface';
-import type { CustomComponentProps } from '../interfaces';
+import type { QItemOverrideComponentProps, SdcUiOverrideComponentProps } from '../interfaces';
import type { ComponentType } from 'react';
/**
@@ -76,6 +76,8 @@ import type { ComponentType } from 'react';
* @property cachedValueSetCodings - Key-value pair of cached value set codings `Record`
* @property fhirPathContext - Key-value pair of evaluated FHIRPath values `Record`
* @property populatedContext - Key-value pair of one-off pre-populated FHIRPath values `Record`
+ * @property qItemOverrideComponents - Key-value pair of React component overrides for Questionnaire Items via linkId `Record`
+ * @property sdcUiOverrideComponents - Key-value pair of React component overrides for SDC UI Controls https://hl7.org/fhir/extensions/ValueSet-questionnaire-item-control.html `Record`
* @property focusedLinkId - LinkId of the currently focused item
* @property readOnly - Flag to set the form to read-only mode
* @property buildSourceQuestionnaire - Used to build the source questionnaire with the provided questionnaire and optionally questionnaire response, additional variables, terminology server url and readyOnly flag
@@ -118,7 +120,8 @@ export interface QuestionnaireStoreType {
cachedValueSetCodings: Record;
fhirPathContext: Record;
populatedContext: Record;
- customComponents: Record>;
+ qItemOverrideComponents: Record>;
+ sdcUiOverrideComponents: Record>;
focusedLinkId: string;
readOnly: boolean;
buildSourceQuestionnaire: (
@@ -127,7 +130,8 @@ export interface QuestionnaireStoreType {
additionalVariables?: Record,
terminologyServerUrl?: string,
readOnly?: boolean,
- customComponents?: Record>
+ qItemOverrideComponents?: Record>,
+ sdcUiOverrideComponents?: Record>
) => Promise;
destroySourceQuestionnaire: () => void;
switchTab: (newTabIndex: number) => void;
@@ -186,7 +190,8 @@ export const questionnaireStore = createStore()((set, ge
cachedValueSetCodings: {},
fhirPathContext: {},
populatedContext: {},
- customComponents: {},
+ qItemOverrideComponents: {},
+ sdcUiOverrideComponents: {},
focusedLinkId: '',
readOnly: false,
buildSourceQuestionnaire: async (
@@ -195,7 +200,8 @@ export const questionnaireStore = createStore()((set, ge
additionalVariables = {},
terminologyServerUrl = terminologyServerStore.getState().url,
readOnly = false,
- customComponents = {}
+ qItemOverrideComponents = {},
+ sdcUiOverrideComponents = {}
) => {
const questionnaireModel = await createQuestionnaireModel(
questionnaire,
@@ -248,7 +254,8 @@ export const questionnaireStore = createStore()((set, ge
processedValueSetCodings: questionnaireModel.processedValueSetCodings,
processedValueSetUrls: questionnaireModel.processedValueSetUrls,
fhirPathContext: updatedFhirPathContext,
- customComponents: customComponents,
+ qItemOverrideComponents: qItemOverrideComponents,
+ sdcUiOverrideComponents: sdcUiOverrideComponents,
readOnly: readOnly
});
},
@@ -272,7 +279,8 @@ export const questionnaireStore = createStore()((set, ge
processedValueSetCodings: {},
processedValueSetUrls: {},
fhirPathContext: {},
- customComponents: {}
+ qItemOverrideComponents: {},
+ sdcUiOverrideComponents: {}
}),
switchTab: (newTabIndex: number) => set(() => ({ currentTabIndex: newTabIndex })),
switchPage: (newPageIndex: number) => set(() => ({ currentPageIndex: newPageIndex })),
diff --git a/packages/smart-forms-renderer/src/stores/rendererStylingStore.ts b/packages/smart-forms-renderer/src/stores/rendererStylingStore.ts
index 65158630..45fc7433 100644
--- a/packages/smart-forms-renderer/src/stores/rendererStylingStore.ts
+++ b/packages/smart-forms-renderer/src/stores/rendererStylingStore.ts
@@ -30,6 +30,7 @@ export interface RendererStyling {
| '800'
| '900'
| 'default';
+ enableWhenAsReadOnly?: boolean | 'non-group'; // fix the non group enablewhen
disablePageCardView?: boolean;
disablePageButtons?: boolean;
}
@@ -51,6 +52,7 @@ export interface RendererStylingStoreType {
| '800'
| '900'
| 'default';
+ enableWhenAsReadOnly: boolean | 'non-group';
disablePageCardView: boolean;
disablePageButtons: boolean;
setRendererStyling: (params: RendererStyling) => void;
@@ -61,11 +63,13 @@ export interface RendererStylingStoreType {
*/
export const rendererStylingStore = createStore()((set) => ({
itemLabelFontWeight: 'default',
+ enableWhenAsReadOnly: false,
disablePageCardView: false,
disablePageButtons: false,
setRendererStyling: (params: RendererStyling) => {
set(() => ({
itemLabelFontWeight: params.itemLabelFontWeight ?? 'default',
+ enableWhenAsReadOnly: params.enableWhenAsReadOnly ?? false,
disablePageCardView: params.disablePageCardView ?? false,
disablePageButtons: params.disablePageButtons ?? false
}));
diff --git a/packages/smart-forms-renderer/src/utils/manageForm.ts b/packages/smart-forms-renderer/src/utils/manageForm.ts
index af625de2..6af8ed9b 100644
--- a/packages/smart-forms-renderer/src/utils/manageForm.ts
+++ b/packages/smart-forms-renderer/src/utils/manageForm.ts
@@ -11,7 +11,8 @@ import { readEncounter, readPatient, readUser } from '../api/smartClient';
import type Client from 'fhirclient/lib/Client';
import { updateQuestionnaireResponse } from './genericRecursive';
import { removeInternalRepeatIdsRecursive } from './removeRepeatId';
-import { ComponentType } from 'react';
+import type { ComponentType } from 'react';
+import type { QItemOverrideComponentProps, SdcUiOverrideComponentProps } from '../interfaces';
/**
* Build the form with an initial Questionnaire and an optional filled QuestionnaireResponse.
@@ -23,7 +24,8 @@ import { ComponentType } from 'react';
* @param readOnly - Applies read-only mode to all items in the form view
* @param terminologyServerUrl - Terminology server url to fetch terminology. If not provided, the default terminology server will be used. (optional)
* @param additionalVariables - Additional key-value pair of SDC variables `Record` for testing (optional)
- * @param customComponents - FIXME add comment
+ * @param qItemOverrideComponents - FIXME add comment
+ * @param sdcUiOverrideComponents - FIXME add comment
*
* @author Sean Fong
*/
@@ -33,7 +35,8 @@ export async function buildForm(
readOnly?: boolean,
terminologyServerUrl?: string,
additionalVariables?: Record,
- customComponents?: Record>
+ qItemOverrideComponents?: Record>,
+ sdcUiOverrideComponents?: Record>
): Promise {
// Reset terminology server
if (terminologyServerUrl) {
@@ -51,7 +54,8 @@ export async function buildForm(
additionalVariables,
terminologyServerUrl,
undefined,
- customComponents
+ qItemOverrideComponents,
+ sdcUiOverrideComponents
);
const initialisedQuestionnaireResponse = initialiseQuestionnaireResponse(