diff --git a/apps/smart-forms-app/cypress/support/commands.ts b/apps/smart-forms-app/cypress/support/commands.ts
index 0be7891b8..82bb5feec 100644
--- a/apps/smart-forms-app/cypress/support/commands.ts
+++ b/apps/smart-forms-app/cypress/support/commands.ts
@@ -62,12 +62,12 @@ Cypress.Commands.add('waitForPopulation', () => {
});
Cypress.Commands.add('launchFromEHRProxy', () => {
- const launchUrl = `${appUrl}/launch?iss=https%3A%2F%2Fproxy.smartforms.io%2Fv%2Fr4%2Ffhir&launch=WzAsInBhdC1zZiIsInByaW1hcnktcGV0ZXIiLCJBVVRPIiwwLDAsMCwiZmhpclVzZXIgb25saW5lX2FjY2VzcyBvcGVuaWQgcHJvZmlsZSBwYXRpZW50L0NvbmRpdGlvbi5ycyBwYXRpZW50L09ic2VydmF0aW9uLnJzIGxhdW5jaCBwYXRpZW50L0VuY291bnRlci5ycyBwYXRpZW50L1F1ZXN0aW9ubmFpcmVSZXNwb25zZS5jcnVkcyBwYXRpZW50L1BhdGllbnQucnMiLCJodHRwOi8vbG9jYWxob3N0OjUxNzMvIiwiMWZmN2JkYzItMzZiMi00MzAzLThjMDUtYzU3MzQyYzViMDQzIiwiIiwiIiwiIiwiIiwwLDEsIiIsZmFsc2Vd`;
+ const launchUrl = `${appUrl}/launch?iss=https%3A%2F%2Fproxy.smartforms.io%2Fv%2Fr4%2Ffhir&launch=WzAsInBhdC1zZiIsInByaW1hcnktcGV0ZXIiLCJBVVRPIiwwLDAsMCwiZmhpclVzZXIgb25saW5lX2FjY2VzcyBvcGVuaWQgcHJvZmlsZSBwYXRpZW50L0NvbmRpdGlvbi5ycyBwYXRpZW50L09ic2VydmF0aW9uLnJzIGxhdW5jaCBwYXRpZW50L0VuY291bnRlci5ycyBwYXRpZW50L1F1ZXN0aW9ubmFpcmVSZXNwb25zZS5jcnVkcyBwYXRpZW50L1BhdGllbnQucnMiLCJodHRwOi8vbG9jYWxob3N0OjQxNzMvIiwiYTU3ZDkwZTMtNWY2OS00YjkyLWFhMmUtMjk5MjE4MDg2M2MxIiwiIiwiIiwiIiwiIiwwLDEsIiIsZmFsc2Vd`;
cy.visit(launchUrl);
});
Cypress.Commands.add('launchFromEHRProxyQuesContext', () => {
- const launchUrl = `${appUrl}/launch?iss=https%3A%2F%2Fproxy.smartforms.io%2Fv%2Fr4%2Ffhir&launch=WzAsInBhdC1zZiIsInByaW1hcnktcGV0ZXIiLCJBVVRPIiwwLDAsMCwiZmhpclVzZXIgb25saW5lX2FjY2VzcyBvcGVuaWQgcHJvZmlsZSBwYXRpZW50L0NvbmRpdGlvbi5ycyBwYXRpZW50L09ic2VydmF0aW9uLnJzIGxhdW5jaCBwYXRpZW50L0VuY291bnRlci5ycyBwYXRpZW50L1F1ZXN0aW9ubmFpcmVSZXNwb25zZS5jcnVkcyBwYXRpZW50L1BhdGllbnQucnMiLCJodHRwOi8vbG9jYWxob3N0OjUxNzMvIiwiMWZmN2JkYzItMzZiMi00MzAzLThjMDUtYzU3MzQyYzViMDQzIiwiIiwiIiwiIiwiIiwwLDEsIntcInJvbGVcIjpcInF1ZXN0aW9ubmFpcmUtcmVuZGVyLW9uLWxhdW5jaFwiLFwiY2Fub25pY2FsXCI6XCJodHRwOi8vd3d3LmhlYWx0aC5nb3YuYXUvYXNzZXNzbWVudHMvbWJzLzcxNXwwLjEuMC1hc3NlbWJsZWRcIixcInR5cGVcIjpcIlF1ZXN0aW9ubmFpcmVcIn0iLGZhbHNlXQ`;
+ const launchUrl = `${appUrl}/launch?iss=https%3A%2F%2Fproxy.smartforms.io%2Fv%2Fr4%2Ffhir&launch=WzAsInBhdC1zZiIsInByaW1hcnktcGV0ZXIiLCJBVVRPIiwwLDAsMCwiZmhpclVzZXIgb25saW5lX2FjY2VzcyBvcGVuaWQgcHJvZmlsZSBwYXRpZW50L0NvbmRpdGlvbi5ycyBwYXRpZW50L09ic2VydmF0aW9uLnJzIGxhdW5jaCBwYXRpZW50L0VuY291bnRlci5ycyBwYXRpZW50L1F1ZXN0aW9ubmFpcmVSZXNwb25zZS5jcnVkcyBwYXRpZW50L1BhdGllbnQucnMiLCJodHRwOi8vbG9jYWxob3N0OjQxNzMvIiwiYTU3ZDkwZTMtNWY2OS00YjkyLWFhMmUtMjk5MjE4MDg2M2MxIiwiIiwiIiwiIiwiIiwwLDEsIntcInJvbGVcIjpcInF1ZXN0aW9ubmFpcmUtcmVuZGVyLW9uLWxhdW5jaFwiLFwiY2Fub25pY2FsXCI6XCJodHRwOi8vd3d3LmhlYWx0aC5nb3YuYXUvYXNzZXNzbWVudHMvbWJzLzcxNXwwLjEuMC1hc3NlbWJsZWRcIixcInR5cGVcIjpcIlF1ZXN0aW9ubmFpcmVcIn0iLGZhbHNlXQ`;
cy.visit(launchUrl);
});
diff --git a/documentation/docs/api/sdc-populate/functions/populate.md b/documentation/docs/api/sdc-populate/functions/populate.md
index 32225b623..51797b083 100644
--- a/documentation/docs/api/sdc-populate/functions/populate.md
+++ b/documentation/docs/api/sdc-populate/functions/populate.md
@@ -1,6 +1,6 @@
# Function: populate()
-> **populate**(`parameters`, `fetchResourceCallback`, `requestConfig`): `Promise` \<[`OutputParameters`](../interfaces/OutputParameters.md) \| `OperationOutcome`\>
+> **populate**(`parameters`, `fetchResourceCallback`, `fetchResourceRequestConfig`, `terminologyCallback`?, `terminologyRequestConfig`?): `Promise` \<[`OutputParameters`](../interfaces/OutputParameters.md) \| `OperationOutcome`\>
Executes the SDC Populate Questionnaire operation - $populate.
Input and output specific parameters conformant to the SDC populate specification. Can be deployed as a $populate microservice.
@@ -13,7 +13,9 @@ This function expects a nice set of populate input parameters to go. If you do y
| :------ | :------ |
| `parameters` | [`InputParameters`](../interfaces/InputParameters.md) |
| `fetchResourceCallback` | [`FetchResourceCallback`](../interfaces/FetchResourceCallback.md) |
-| `requestConfig` | `any` |
+| `fetchResourceRequestConfig` | `any` |
+| `terminologyCallback`? | [`FetchResourceCallback`](../interfaces/FetchResourceCallback.md) |
+| `terminologyRequestConfig`? | `any` |
## Returns
diff --git a/documentation/docs/api/sdc-populate/interfaces/PopulateQuestionnaireParams.md b/documentation/docs/api/sdc-populate/interfaces/PopulateQuestionnaireParams.md
index c7e7c9c8c..5fd87535f 100644
--- a/documentation/docs/api/sdc-populate/interfaces/PopulateQuestionnaireParams.md
+++ b/documentation/docs/api/sdc-populate/interfaces/PopulateQuestionnaireParams.md
@@ -42,6 +42,22 @@ Any request configuration to be passed to the fetchResourceCallback i.e. headers
***
+### terminologyCallback?
+
+> `optional` **terminologyCallback**: [`FetchResourceCallback`](FetchResourceCallback.md)
+
+A callback function to fetch terminology resources, optional
+
+***
+
+### terminologyRequestConfig?
+
+> `optional` **terminologyRequestConfig**: `any`
+
+Any request configuration to be passed to the terminologyCallback i.e. headers, auth etc., optional
+
+***
+
### user?
> `optional` **user**: `Practitioner`
diff --git a/documentation/docs/components/display.mdx b/documentation/docs/components/display.mdx
index 6a299b967..3ee9d808e 100644
--- a/documentation/docs/components/display.mdx
+++ b/documentation/docs/components/display.mdx
@@ -30,6 +30,16 @@ import IframeContainer from '../../src/react/IframeContainer';
/>
+#### Calculations with [cqf-expression](/docs/sdc/calculations#cqf-expression) styled with [rendering-style](/docs/sdc/advanced/display#rendering-style)
+
+
+
+
+
There are SDC advanced form rendering usages in the SDC [Display/Text Rendering](/docs/sdc/advanced/display) section.
If there are any advanced usage scenarios you'd like to see, feel free to [let us know](https://github.com/aehrc/smart-forms/issues/new).
diff --git a/documentation/docs/sdc/advanced/display.mdx b/documentation/docs/sdc/advanced/display.mdx
index 45d5dc3ae..7a4313099 100644
--- a/documentation/docs/sdc/advanced/display.mdx
+++ b/documentation/docs/sdc/advanced/display.mdx
@@ -155,6 +155,32 @@ This mechanic doesn't interact with the "Previous/Next Tab" buttons. You can sti
:::
+### Rendering Style
+
+Allows styling of the item's label using CSS styles. This is useful when the item's label needs to be styled differently from the rest of the form to draw attention to it.
+
+For more information, refer to http://hl7.org/fhir/uv/sdc/rendering.html#rendering-style.
+
+#### Basic Usage
+
+
+
+
+
+#### Calculations Usage
+
+
+
+
+
### Rendering XHTML
Provides an alternate representation of the item's label as a XHTML string. Allows for the rendering of styled text and base64-encoded images.
diff --git a/package-lock.json b/package-lock.json
index bd0756fea..02da1465e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -43963,6 +43963,7 @@
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
"react-markdown": "^8.0.7",
+ "style-to-object": "^1.0.6",
"zustand": "^4.5.2"
},
"devDependencies": {
@@ -44750,6 +44751,11 @@
"node": ">=10.17.0"
}
},
+ "packages/smart-forms-renderer/node_modules/inline-style-parser": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.3.tgz",
+ "integrity": "sha512-qlD8YNDqyTKTyuITrDOffsl6Tdhv+UC4hcdAVuQsK4IMQ99nSgd1MIA/Q+jQYoh9r3hVUXhYh7urSRmXPkW04g=="
+ },
"packages/smart-forms-renderer/node_modules/jscodeshift": {
"version": "0.15.2",
"resolved": "https://registry.npmjs.org/jscodeshift/-/jscodeshift-0.15.2.tgz",
@@ -44875,6 +44881,14 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "packages/smart-forms-renderer/node_modules/style-to-object": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.6.tgz",
+ "integrity": "sha512-khxq+Qm3xEyZfKd/y9L3oIWQimxuc4STrQKtQn8aSDRHb8mFgpukgX1hdzfrMEW6JCjyJ8p89x+IUMVnCBI1PA==",
+ "dependencies": {
+ "inline-style-parser": "0.2.3"
+ }
+ },
"packages/smart-forms-renderer/node_modules/write-file-atomic": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz",
diff --git a/packages/smart-forms-renderer/package.json b/packages/smart-forms-renderer/package.json
index 9aff56b25..94bb50109 100644
--- a/packages/smart-forms-renderer/package.json
+++ b/packages/smart-forms-renderer/package.json
@@ -42,6 +42,7 @@
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
"react-markdown": "^8.0.7",
+ "style-to-object": "^1.0.6",
"zustand": "^4.5.2"
},
"peerDependencies": {
diff --git a/packages/smart-forms-renderer/src/components/FormComponents/ItemParts/ItemLabelText.tsx b/packages/smart-forms-renderer/src/components/FormComponents/ItemParts/ItemLabelText.tsx
index 0095e2dc7..1a5388c0e 100644
--- a/packages/smart-forms-renderer/src/components/FormComponents/ItemParts/ItemLabelText.tsx
+++ b/packages/smart-forms-renderer/src/components/FormComponents/ItemParts/ItemLabelText.tsx
@@ -18,11 +18,13 @@
import React, { memo } from 'react';
import type { QuestionnaireItem } from 'fhir/r4';
import { getMarkdownString, getXHtmlString } from '../../../utils/itemControl';
-import parse from 'html-react-parser';
+import { default as htmlParse } from 'html-react-parser';
import Box from '@mui/material/Box';
import ReactMarkdown from 'react-markdown';
import Typography from '@mui/material/Typography';
import useDisplayCqfAndCalculatedExpression from '../../../hooks/useDisplayCqfAndCalculatedExpression';
+import { structuredDataCapture } from 'fhir-sdc-helpers';
+import { default as styleParse } from 'style-to-js';
interface ItemLabelTextProps {
qItem: QuestionnaireItem;
@@ -32,21 +34,18 @@ interface ItemLabelTextProps {
const ItemLabelText = memo(function ItemLabelText(props: ItemLabelTextProps) {
const { qItem, readOnly } = props;
+ let labelText = qItem.text ?? '';
+
// Use calculatedExpressionString if available
- const calculatedExpressionString = useDisplayCqfAndCalculatedExpression(qItem);
+ const calculatedExpressionString = useDisplayCqfAndCalculatedExpression(qItem) ?? '';
if (calculatedExpressionString) {
- return (
-
- {calculatedExpressionString}
-
- );
+ labelText = calculatedExpressionString;
}
// parse xHTML if found
const xHtmlString = getXHtmlString(qItem);
-
if (xHtmlString) {
- return {parse(xHtmlString)};
+ return {htmlParse(xHtmlString)};
}
// parse markdown if found
@@ -59,14 +58,26 @@ const ItemLabelText = memo(function ItemLabelText(props: ItemLabelTextProps) {
);
}
+ // labelText is empty, return null
+ if (!labelText) {
+ return null;
+ }
+
+ // parse styles if found
+ const stylesString = structuredDataCapture.getStyle(qItem._text);
+ if (stylesString) {
+ const styles = styleParse(stylesString);
+ return
{labelText}
;
+ }
+
if (qItem.type === 'group') {
- return <>{qItem.text}>;
+ return <>{labelText}>;
}
// parse regular text
return (
- {qItem.text}
+ {labelText}
);
});
diff --git a/packages/smart-forms-renderer/src/stories/assets/questionnaires/QAdvancedTextApperance.ts b/packages/smart-forms-renderer/src/stories/assets/questionnaires/QAdvancedTextApperance.ts
index 9a2f9238e..f474c8a2a 100644
--- a/packages/smart-forms-renderer/src/stories/assets/questionnaires/QAdvancedTextApperance.ts
+++ b/packages/smart-forms-renderer/src/stories/assets/questionnaires/QAdvancedTextApperance.ts
@@ -1,5 +1,34 @@
import type { Questionnaire } from 'fhir/r4';
+export const qRenderingStyleBooleanCheckboxItem: Questionnaire = {
+ resourceType: 'Questionnaire',
+ id: 'RenderingStyle-1',
+ name: 'RenderingStyle-1',
+ title: 'Rendering Style 1',
+ version: '0.1.0',
+ status: 'draft',
+ publisher: 'AEHRC CSIRO',
+ date: '2024-05-01',
+ url: 'https://smartforms.csiro.au/docs/advanced/text/rendering-style-1',
+ item: [
+ {
+ linkId: 'mark-complete',
+ text: 'Mark section as complete',
+ _text: {
+ extension: [
+ {
+ url: 'http://hl7.org/fhir/StructureDefinition/rendering-style',
+ valueString:
+ 'padding: 0.75rem; margin-bottom: 1rem; font-size: 0.875rem; color: #2E7D32; border-radius: 0.5rem; background-color: #d5e5d6; font-weight: 700;'
+ }
+ ]
+ },
+ type: 'boolean',
+ repeats: false
+ }
+ ]
+};
+
export const qRenderingXhtmlBooleanCheckboxItem: Questionnaire = {
resourceType: 'Questionnaire',
id: 'RenderingXhtml-1',
diff --git a/packages/smart-forms-renderer/src/stories/assets/questionnaires/QDisplay.ts b/packages/smart-forms-renderer/src/stories/assets/questionnaires/QDisplay.ts
index d32b8733c..9e9ccbfb1 100644
--- a/packages/smart-forms-renderer/src/stories/assets/questionnaires/QDisplay.ts
+++ b/packages/smart-forms-renderer/src/stories/assets/questionnaires/QDisplay.ts
@@ -113,3 +113,85 @@ export const qDisplayCalculation: Questionnaire = {
}
]
};
+
+export const qDisplayCalculationStyled: Questionnaire = {
+ resourceType: 'Questionnaire',
+ id: 'DisplayCalculationStyled',
+ name: 'DisplayCalculationStyled',
+ title: 'Display Calculation Styled',
+ version: '0.1.0',
+ status: 'draft',
+ publisher: 'AEHRC CSIRO',
+ date: '2024-05-01',
+ url: 'https://smartforms.csiro.au/docs/components/display/calculation-styled',
+ extension: [
+ {
+ url: 'http://hl7.org/fhir/StructureDefinition/variable',
+ valueExpression: {
+ name: 'gender',
+ language: 'text/fhirpath',
+ expression: "item.where(linkId = 'gender-controller').answer.valueCoding.code"
+ }
+ }
+ ],
+ item: [
+ {
+ linkId: 'gender-controller',
+ text: 'Gender',
+ type: 'choice',
+ repeats: false,
+ answerOption: [
+ {
+ valueCoding: {
+ system: 'http://hl7.org/fhir/administrative-gender',
+ code: 'female',
+ display: 'Female'
+ }
+ },
+ {
+ valueCoding: {
+ system: 'http://hl7.org/fhir/administrative-gender',
+ code: 'male',
+ display: 'Male'
+ }
+ },
+ {
+ valueCoding: {
+ system: 'http://hl7.org/fhir/administrative-gender',
+ code: 'other',
+ display: 'Other'
+ }
+ },
+ {
+ valueCoding: {
+ system: 'http://hl7.org/fhir/administrative-gender',
+ code: 'unknown',
+ display: 'Unknown'
+ }
+ }
+ ]
+ },
+ {
+ linkId: 'gender-display',
+ type: 'display',
+ repeats: false,
+ _text: {
+ extension: [
+ {
+ url: 'http://hl7.org/fhir/StructureDefinition/cqf-expression',
+ valueExpression: {
+ language: 'text/fhirpath',
+ expression: "'Gender: '+ %gender"
+ }
+ },
+ {
+ url: 'http://hl7.org/fhir/StructureDefinition/rendering-style',
+ valueString:
+ 'padding: 0.75rem; margin-bottom: 1rem; font-size: 0.875rem; color: #2E7D32; border-radius: 0.5rem; background-color: #d5e5d6; font-weight: 700;'
+ }
+ ]
+ },
+ text: ''
+ }
+ ]
+};
diff --git a/packages/smart-forms-renderer/src/stories/itemTypes/Display.stories.tsx b/packages/smart-forms-renderer/src/stories/itemTypes/Display.stories.tsx
index d0d1930bf..886c7e17a 100644
--- a/packages/smart-forms-renderer/src/stories/itemTypes/Display.stories.tsx
+++ b/packages/smart-forms-renderer/src/stories/itemTypes/Display.stories.tsx
@@ -17,7 +17,11 @@
import type { Meta, StoryObj } from '@storybook/react';
import BuildFormWrapperForStorybook from '../storybookWrappers/BuildFormWrapperForStorybook';
-import { qDisplayBasic, qDisplayCalculation } from '../assets/questionnaires/QDisplay';
+import {
+ qDisplayBasic,
+ qDisplayCalculation,
+ qDisplayCalculationStyled
+} from '../assets/questionnaires/QDisplay';
// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction#default-export
const meta = {
@@ -43,3 +47,9 @@ export const DisplayCalculation: Story = {
questionnaire: qDisplayCalculation
}
};
+
+export const DisplayCalculationStyled: Story = {
+ args: {
+ questionnaire: qDisplayCalculationStyled
+ }
+};
diff --git a/packages/smart-forms-renderer/src/stories/sdc/AdvancedTextAppearance.stories.tsx b/packages/smart-forms-renderer/src/stories/sdc/AdvancedTextAppearance.stories.tsx
index df66efcdc..074fb7797 100644
--- a/packages/smart-forms-renderer/src/stories/sdc/AdvancedTextAppearance.stories.tsx
+++ b/packages/smart-forms-renderer/src/stories/sdc/AdvancedTextAppearance.stories.tsx
@@ -21,6 +21,7 @@ import {
qDisplayCategoryInstructions,
qHidden,
qOpenLabel,
+ qRenderingStyleBooleanCheckboxItem,
qRenderingXhtmlBooleanCheckboxItem,
qRenderingXhtmlDisplayBase64ImageItem,
qRenderingXhtmlDisplayListItem
@@ -39,6 +40,12 @@ type Story = StoryObj;
// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args
+export const RenderingStyleBooleanCheckbox: Story = {
+ args: {
+ questionnaire: qRenderingStyleBooleanCheckboxItem
+ }
+};
+
export const RenderingXHTMLBooleanCheckbox: Story = {
args: {
questionnaire: qRenderingXhtmlBooleanCheckboxItem