@aehrc/smart-forms-renderer 1.2.7 → 1.2.9
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.
- package/lib/components/FormComponents/AttachmentItem/AttachmentField.js +3 -2
- package/lib/components/FormComponents/AttachmentItem/AttachmentField.js.map +1 -1
- package/lib/components/FormComponents/BooleanItem/BooleanField.d.ts +2 -1
- package/lib/components/FormComponents/BooleanItem/BooleanField.js +6 -4
- package/lib/components/FormComponents/BooleanItem/BooleanField.js.map +1 -1
- package/lib/components/FormComponents/ChoiceItems/ChoiceSelectAnswerOptionFields.js +8 -8
- package/lib/components/FormComponents/ChoiceItems/ChoiceSelectAnswerOptionFields.js.map +1 -1
- package/lib/components/FormComponents/ChoiceItems/ChoiceSelectAnswerValueSetFields.js +11 -11
- package/lib/components/FormComponents/ChoiceItems/ChoiceSelectAnswerValueSetFields.js.map +1 -1
- package/lib/components/FormComponents/DateTimeItems/CustomDateItem/CustomDateField.js +2 -1
- package/lib/components/FormComponents/DateTimeItems/CustomDateItem/CustomDateField.js.map +1 -1
- package/lib/components/FormComponents/DateTimeItems/CustomDateTimeItem/CustomTimeField.js +2 -1
- package/lib/components/FormComponents/DateTimeItems/CustomDateTimeItem/CustomTimeField.js.map +1 -1
- package/lib/components/FormComponents/DecimalItem/DecimalField.js +2 -1
- package/lib/components/FormComponents/DecimalItem/DecimalField.js.map +1 -1
- package/lib/components/FormComponents/GroupItem/GroupHeading.js +1 -1
- package/lib/components/FormComponents/GroupItem/GroupHeading.js.map +1 -1
- package/lib/components/FormComponents/IntegerItem/IntegerField.js +2 -1
- package/lib/components/FormComponents/IntegerItem/IntegerField.js.map +1 -1
- package/lib/components/FormComponents/Item.styles.d.ts +1 -1
- package/lib/components/FormComponents/Item.styles.js +1 -1
- package/lib/components/FormComponents/ItemParts/AccessibleFeedback.d.ts +7 -0
- package/lib/components/FormComponents/ItemParts/AccessibleFeedback.js +7 -0
- package/lib/components/FormComponents/ItemParts/AccessibleFeedback.js.map +1 -0
- package/lib/components/FormComponents/ItemParts/CheckboxFormGroup.js +4 -2
- package/lib/components/FormComponents/ItemParts/CheckboxFormGroup.js.map +1 -1
- package/lib/components/FormComponents/ItemParts/ClearButtonAdornment.js +12 -12
- package/lib/components/FormComponents/ItemParts/ClearButtonAdornment.js.map +1 -1
- package/lib/components/FormComponents/ItemParts/RadioFormGroup.d.ts +1 -1
- package/lib/components/FormComponents/ItemParts/RadioFormGroup.js +5 -3
- package/lib/components/FormComponents/ItemParts/RadioFormGroup.js.map +1 -1
- package/lib/components/FormComponents/OpenChoiceItems/OpenChoiceSelectAnswerOptionField.js +8 -8
- package/lib/components/FormComponents/OpenChoiceItems/OpenChoiceSelectAnswerOptionField.js.map +1 -1
- package/lib/components/FormComponents/OpenChoiceItems/OpenChoiceSelectAnswerValueSetField.js +3 -3
- package/lib/components/FormComponents/OpenChoiceItems/OpenChoiceSelectAnswerValueSetField.js.map +1 -1
- package/lib/components/FormComponents/QuantityItem/QuantityField.js +2 -1
- package/lib/components/FormComponents/QuantityItem/QuantityField.js.map +1 -1
- package/lib/components/FormComponents/SliderItem/SliderField.js +4 -2
- package/lib/components/FormComponents/SliderItem/SliderField.js.map +1 -1
- package/lib/components/FormComponents/StringItem/StringField.js +2 -1
- package/lib/components/FormComponents/StringItem/StringField.js.map +1 -1
- package/lib/components/FormComponents/TextItem/TextField.js +2 -1
- package/lib/components/FormComponents/TextItem/TextField.js.map +1 -1
- package/lib/components/FormComponents/UrlItem/UrlField.js +2 -1
- package/lib/components/FormComponents/UrlItem/UrlField.js.map +1 -1
- package/lib/components/Tabs/FormBodySingleTab.js +6 -1
- package/lib/components/Tabs/FormBodySingleTab.js.map +1 -1
- package/package.json +4 -4
- package/src/components/FormComponents/AttachmentItem/AttachmentField.tsx +7 -2
- package/src/components/FormComponents/BooleanItem/BooleanField.tsx +12 -3
- package/src/components/FormComponents/ChoiceItems/ChoiceSelectAnswerOptionFields.tsx +2 -3
- package/src/components/FormComponents/ChoiceItems/ChoiceSelectAnswerValueSetFields.tsx +3 -3
- package/src/components/FormComponents/DateTimeItems/CustomDateItem/CustomDateField.tsx +2 -1
- package/src/components/FormComponents/DateTimeItems/CustomDateTimeItem/CustomTimeField.tsx +6 -3
- package/src/components/FormComponents/DecimalItem/DecimalField.tsx +2 -1
- package/src/components/FormComponents/GroupItem/GroupHeading.tsx +8 -9
- package/src/components/FormComponents/IntegerItem/IntegerField.tsx +2 -1
- package/src/components/FormComponents/Item.styles.ts +1 -1
- package/src/components/FormComponents/ItemParts/AccessibleFeedback.tsx +35 -0
- package/src/components/FormComponents/ItemParts/CheckboxFormGroup.tsx +10 -2
- package/src/components/FormComponents/ItemParts/ClearButtonAdornment.tsx +22 -23
- package/src/components/FormComponents/ItemParts/RadioFormGroup.tsx +11 -3
- package/src/components/FormComponents/OpenChoiceItems/OpenChoiceSelectAnswerOptionField.tsx +2 -3
- package/src/components/FormComponents/OpenChoiceItems/OpenChoiceSelectAnswerValueSetField.tsx +2 -3
- package/src/components/FormComponents/QuantityItem/QuantityField.tsx +2 -1
- package/src/components/FormComponents/SliderItem/SliderField.tsx +10 -2
- package/src/components/FormComponents/StringItem/StringField.tsx +2 -1
- package/src/components/FormComponents/TextItem/TextField.tsx +2 -1
- package/src/components/FormComponents/UrlItem/UrlField.tsx +2 -1
- package/src/components/Tabs/FormBodySingleTab.tsx +28 -8
|
@@ -64,15 +64,6 @@ const GroupHeading = memo(function GroupHeading(props: GroupHeadingProps) {
|
|
|
64
64
|
<>
|
|
65
65
|
<Box display="flex" alignItems="center" width="100%">
|
|
66
66
|
<Box position="relative" display="flex" flexGrow={1} alignItems="center">
|
|
67
|
-
{/* Required asterisk position is in front of text */}
|
|
68
|
-
{required && requiredIndicatorPosition === 'start' ? (
|
|
69
|
-
<RequiredAsterisk
|
|
70
|
-
sx={{ position: 'absolute', top: 0, left: -8 }} // Adjust top and left values as needed
|
|
71
|
-
>
|
|
72
|
-
*
|
|
73
|
-
</RequiredAsterisk>
|
|
74
|
-
) : null}
|
|
75
|
-
|
|
76
67
|
{/* Group Heading typography */}
|
|
77
68
|
{/* flexGrow: 1 is important if xhtml and markdown rendering has width: 100% */}
|
|
78
69
|
<Typography
|
|
@@ -82,6 +73,14 @@ const GroupHeading = memo(function GroupHeading(props: GroupHeadingProps) {
|
|
|
82
73
|
display="flex"
|
|
83
74
|
alignItems="center"
|
|
84
75
|
sx={{ flexGrow: 1, ...(parentStyles || {}) }}>
|
|
76
|
+
{/* Required asterisk position is in front of text */}
|
|
77
|
+
{required && requiredIndicatorPosition === 'start' ? (
|
|
78
|
+
<RequiredAsterisk
|
|
79
|
+
sx={{ position: 'absolute', top: 0, left: -8 }} // Adjust top and left values as needed
|
|
80
|
+
>
|
|
81
|
+
*
|
|
82
|
+
</RequiredAsterisk>
|
|
83
|
+
) : null}
|
|
85
84
|
<ItemTextSwitcher qItem={qItem} />
|
|
86
85
|
|
|
87
86
|
{/* Required asterisk position is behind text */}
|
|
@@ -25,6 +25,7 @@ import type { QuestionnaireItem, QuestionnaireResponseItem } from 'fhir/r4';
|
|
|
25
25
|
import ItemRepopulateButton from '../ItemParts/ItemRepopulateButton';
|
|
26
26
|
import type { RenderingExtensions } from '../../../hooks/useRenderingExtensions';
|
|
27
27
|
import { StandardTextField } from '../Textfield.styles';
|
|
28
|
+
import AccessibleFeedback from '../ItemParts/AccessibleFeedback';
|
|
28
29
|
|
|
29
30
|
interface IntegerFieldProps extends PropsWithIsTabledAttribute {
|
|
30
31
|
qItem: QuestionnaireItem;
|
|
@@ -74,7 +75,7 @@ function IntegerField(props: IntegerFieldProps) {
|
|
|
74
75
|
id={inputId}
|
|
75
76
|
value={input}
|
|
76
77
|
error={!!feedback}
|
|
77
|
-
helperText={feedback}
|
|
78
|
+
helperText={<AccessibleFeedback>{feedback}</AccessibleFeedback>}
|
|
78
79
|
onChange={(event) => onInputChange(event.target.value)}
|
|
79
80
|
disabled={readOnly && readOnlyVisualStyle === 'disabled'}
|
|
80
81
|
label={displayPrompt}
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import { styled } from '@mui/material/styles';
|
|
19
19
|
import Typography from '@mui/material/Typography';
|
|
20
20
|
|
|
21
|
-
export const
|
|
21
|
+
export const StyledFeedbackTypography = styled(Typography)(({ theme }) => ({
|
|
22
22
|
color: theme.palette.error.main,
|
|
23
23
|
fontSize: '0.75rem',
|
|
24
24
|
marginTop: 4
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 Commonwealth Scientific and Industrial Research
|
|
3
|
+
* Organisation (CSIRO) ABN 41 687 119 230.
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { type ReactNode } from 'react';
|
|
19
|
+
|
|
20
|
+
interface AccessibleFeedbackProps {
|
|
21
|
+
children: ReactNode;
|
|
22
|
+
id?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function AccessibleFeedback(props: AccessibleFeedbackProps) {
|
|
26
|
+
const { children, id } = props;
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<span id={id} role="alert" aria-live="assertive">
|
|
30
|
+
{children}
|
|
31
|
+
</span>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export default AccessibleFeedback;
|
|
@@ -10,9 +10,10 @@ import { ChoiceItemOrientation } from '../../../interfaces/choice.enum';
|
|
|
10
10
|
import { useRendererConfigStore } from '../../../stores';
|
|
11
11
|
import { getChoiceOrientation } from '../../../utils/choice';
|
|
12
12
|
import CheckboxOptionList from '../ChoiceItems/CheckboxOptionList';
|
|
13
|
-
import {
|
|
13
|
+
import { StyledFeedbackTypography } from '../Item.styles';
|
|
14
14
|
import ClearInputButton from './ClearInputButton';
|
|
15
15
|
import ExpressionUpdateFadingIcon from './ExpressionUpdateFadingIcon';
|
|
16
|
+
import AccessibleFeedback from './AccessibleFeedback';
|
|
16
17
|
|
|
17
18
|
interface ChoiceCheckboxFormGroupProps {
|
|
18
19
|
qItem: QuestionnaireItem;
|
|
@@ -50,6 +51,8 @@ function CheckboxFormGroup(props: ChoiceCheckboxFormGroupProps) {
|
|
|
50
51
|
|
|
51
52
|
const answersEmpty = answers.length === 0;
|
|
52
53
|
|
|
54
|
+
const feedbackId = `${qItem.type}-${qItem.linkId}-feedback`;
|
|
55
|
+
|
|
53
56
|
return (
|
|
54
57
|
<>
|
|
55
58
|
<Box
|
|
@@ -68,6 +71,7 @@ function CheckboxFormGroup(props: ChoiceCheckboxFormGroupProps) {
|
|
|
68
71
|
{...(!isTabled
|
|
69
72
|
? { 'aria-labelledby': 'label-' + qItem.linkId }
|
|
70
73
|
: { 'aria-label': qItem.text ?? 'Unnamed checkbox list' })}
|
|
74
|
+
aria-describedby={feedback ? feedbackId : undefined}
|
|
71
75
|
role="group"
|
|
72
76
|
row={orientation === ChoiceItemOrientation.Horizontal}
|
|
73
77
|
sx={inputsFlexGrow ? { width: '100%', flexWrap: 'nowrap' } : {}}>
|
|
@@ -91,7 +95,11 @@ function CheckboxFormGroup(props: ChoiceCheckboxFormGroupProps) {
|
|
|
91
95
|
<ClearInputButton buttonShown={!answersEmpty} readOnly={readOnly} onClear={onClear} />
|
|
92
96
|
</Box>
|
|
93
97
|
|
|
94
|
-
{feedback ?
|
|
98
|
+
{feedback ? (
|
|
99
|
+
<AccessibleFeedback id={feedbackId}>
|
|
100
|
+
<StyledFeedbackTypography>{feedback}</StyledFeedbackTypography>
|
|
101
|
+
</AccessibleFeedback>
|
|
102
|
+
) : null}
|
|
95
103
|
</>
|
|
96
104
|
);
|
|
97
105
|
}
|
|
@@ -14,29 +14,28 @@ export function ClearButtonAdornment(props: ClearButtonAdornmentProps) {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
return (
|
|
17
|
-
<
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
<IconButton
|
|
18
|
+
aria-hidden="true"
|
|
19
|
+
tabIndex={-1}
|
|
20
|
+
size="small"
|
|
21
|
+
title="Clear"
|
|
22
|
+
onClick={(e) => {
|
|
23
|
+
onClear();
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
</span>
|
|
25
|
+
// Manually re-focus the input field
|
|
26
|
+
const input = e.currentTarget.closest('.MuiOutlinedInput-root')?.querySelector('input');
|
|
27
|
+
if (input) {
|
|
28
|
+
input.focus();
|
|
29
|
+
}
|
|
30
|
+
}}
|
|
31
|
+
onMouseDown={(e) => {
|
|
32
|
+
e.preventDefault(); // Prevent the button from stealing focus
|
|
33
|
+
}}
|
|
34
|
+
className="StandardTextField-clearIndicator"
|
|
35
|
+
sx={{
|
|
36
|
+
p: 0.5
|
|
37
|
+
}}>
|
|
38
|
+
<ClearIcon fontSize="small" />
|
|
39
|
+
</IconButton>
|
|
41
40
|
);
|
|
42
41
|
}
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import Box from '@mui/material/Box';
|
|
2
2
|
import RadioGroup from '@mui/material/RadioGroup';
|
|
3
3
|
import type { QuestionnaireItem, QuestionnaireItemAnswerOption } from 'fhir/r4';
|
|
4
|
-
import
|
|
4
|
+
import React, { ReactNode } from 'react';
|
|
5
5
|
import { ChoiceItemOrientation } from '../../../interfaces/choice.enum';
|
|
6
6
|
import { useRendererConfigStore } from '../../../stores';
|
|
7
7
|
import { getChoiceOrientation } from '../../../utils/choice';
|
|
8
|
-
import {
|
|
8
|
+
import { StyledFeedbackTypography } from '../Item.styles';
|
|
9
9
|
import ClearInputButton from './ClearInputButton';
|
|
10
10
|
import ExpressionUpdateFadingIcon from './ExpressionUpdateFadingIcon';
|
|
11
11
|
import RadioOptionList from './RadioOptionList';
|
|
12
|
+
import AccessibleFeedback from './AccessibleFeedback';
|
|
12
13
|
|
|
13
14
|
interface ChoiceRadioGroupProps {
|
|
14
15
|
qItem: QuestionnaireItem;
|
|
@@ -44,6 +45,8 @@ function RadioFormGroup(props: ChoiceRadioGroupProps) {
|
|
|
44
45
|
|
|
45
46
|
const orientation = getChoiceOrientation(qItem) ?? ChoiceItemOrientation.Vertical;
|
|
46
47
|
|
|
48
|
+
const feedbackId = qItem.type + '-' + qItem.linkId + '-feedback';
|
|
49
|
+
|
|
47
50
|
return (
|
|
48
51
|
<>
|
|
49
52
|
<Box
|
|
@@ -64,6 +67,7 @@ function RadioFormGroup(props: ChoiceRadioGroupProps) {
|
|
|
64
67
|
? { 'aria-labelledby': 'label-' + qItem.linkId }
|
|
65
68
|
: { 'aria-label': qItem.text ?? 'Unnamed radio group' })}
|
|
66
69
|
aria-readonly={readOnly && readOnlyVisualStyle === 'readonly'}
|
|
70
|
+
aria-describedby={feedback ? feedbackId : undefined}
|
|
67
71
|
row={orientation === ChoiceItemOrientation.Horizontal}
|
|
68
72
|
sx={inputsFlexGrow ? { width: '100%', flexWrap: 'nowrap' } : {}}
|
|
69
73
|
onChange={(e) => {
|
|
@@ -93,7 +97,11 @@ function RadioFormGroup(props: ChoiceRadioGroupProps) {
|
|
|
93
97
|
<ClearInputButton buttonShown={!!valueRadio} readOnly={readOnly} onClear={onClear} />
|
|
94
98
|
</Box>
|
|
95
99
|
|
|
96
|
-
{feedback ?
|
|
100
|
+
{feedback ? (
|
|
101
|
+
<AccessibleFeedback id={feedbackId}>
|
|
102
|
+
<StyledFeedbackTypography>{feedback}</StyledFeedbackTypography>
|
|
103
|
+
</AccessibleFeedback>
|
|
104
|
+
) : null}
|
|
97
105
|
</>
|
|
98
106
|
);
|
|
99
107
|
}
|
|
@@ -27,8 +27,8 @@ import type {
|
|
|
27
27
|
PropsWithRenderingExtensionsAttribute
|
|
28
28
|
} from '../../../interfaces/renderProps.interface';
|
|
29
29
|
import { useRendererConfigStore } from '../../../stores';
|
|
30
|
-
import { StyledRequiredTypography } from '../Item.styles';
|
|
31
30
|
import DisplayUnitText from '../ItemParts/DisplayUnitText';
|
|
31
|
+
import AccessibleFeedback from '../ItemParts/AccessibleFeedback';
|
|
32
32
|
|
|
33
33
|
interface OpenChoiceSelectAnswerOptionFieldProps
|
|
34
34
|
extends PropsWithIsTabledAttribute,
|
|
@@ -83,6 +83,7 @@ function OpenChoiceSelectAnswerOptionField(props: OpenChoiceSelectAnswerOptionFi
|
|
|
83
83
|
textFieldWidth={textFieldWidth}
|
|
84
84
|
isTabled={isTabled}
|
|
85
85
|
placeholder={entryFormat || displayPrompt}
|
|
86
|
+
helperText={<AccessibleFeedback>{feedback}</AccessibleFeedback>}
|
|
86
87
|
{...params}
|
|
87
88
|
slotProps={{
|
|
88
89
|
input: {
|
|
@@ -99,8 +100,6 @@ function OpenChoiceSelectAnswerOptionField(props: OpenChoiceSelectAnswerOptionFi
|
|
|
99
100
|
/>
|
|
100
101
|
)}
|
|
101
102
|
/>
|
|
102
|
-
|
|
103
|
-
{feedback ? <StyledRequiredTypography>{feedback}</StyledRequiredTypography> : null}
|
|
104
103
|
</>
|
|
105
104
|
);
|
|
106
105
|
}
|
package/src/components/FormComponents/OpenChoiceItems/OpenChoiceSelectAnswerValueSetField.tsx
CHANGED
|
@@ -28,8 +28,8 @@ import type {
|
|
|
28
28
|
import type { Coding, QuestionnaireItem } from 'fhir/r4';
|
|
29
29
|
import type { TerminologyError } from '../../../hooks/useValueSetCodings';
|
|
30
30
|
import { useRendererConfigStore } from '../../../stores';
|
|
31
|
-
import { StyledRequiredTypography } from '../Item.styles';
|
|
32
31
|
import DisplayUnitText from '../ItemParts/DisplayUnitText';
|
|
32
|
+
import AccessibleFeedback from '../ItemParts/AccessibleFeedback';
|
|
33
33
|
|
|
34
34
|
interface OpenChoiceSelectAnswerValueSetFieldProps
|
|
35
35
|
extends PropsWithIsTabledAttribute,
|
|
@@ -88,6 +88,7 @@ function OpenChoiceSelectAnswerValueSetField(props: OpenChoiceSelectAnswerValueS
|
|
|
88
88
|
textFieldWidth={textFieldWidth}
|
|
89
89
|
isTabled={isTabled}
|
|
90
90
|
placeholder={entryFormat || displayPrompt}
|
|
91
|
+
helperText={<AccessibleFeedback>{feedback}</AccessibleFeedback>}
|
|
91
92
|
{...params}
|
|
92
93
|
slotProps={{
|
|
93
94
|
input: {
|
|
@@ -110,8 +111,6 @@ function OpenChoiceSelectAnswerValueSetField(props: OpenChoiceSelectAnswerValueS
|
|
|
110
111
|
{terminologyError.answerValueSet}
|
|
111
112
|
</Typography>
|
|
112
113
|
) : null}
|
|
113
|
-
|
|
114
|
-
{feedback ? <StyledRequiredTypography>{feedback}</StyledRequiredTypography> : null}
|
|
115
114
|
</>
|
|
116
115
|
);
|
|
117
116
|
}
|
|
@@ -22,6 +22,7 @@ import DisplayUnitText from '../ItemParts/DisplayUnitText';
|
|
|
22
22
|
import { ClearButtonAdornment } from '../ItemParts/ClearButtonAdornment';
|
|
23
23
|
import ExpressionUpdateFadingIcon from '../ItemParts/ExpressionUpdateFadingIcon';
|
|
24
24
|
import { StandardTextField } from '../Textfield.styles';
|
|
25
|
+
import AccessibleFeedback from '../ItemParts/AccessibleFeedback';
|
|
25
26
|
|
|
26
27
|
interface QuantityFieldProps extends PropsWithIsTabledAttribute {
|
|
27
28
|
linkId: string;
|
|
@@ -104,7 +105,7 @@ function QuantityField(props: QuantityFieldProps) {
|
|
|
104
105
|
)
|
|
105
106
|
}
|
|
106
107
|
}}
|
|
107
|
-
helperText={feedback}
|
|
108
|
+
helperText={<AccessibleFeedback>{feedback}</AccessibleFeedback>}
|
|
108
109
|
data-test="q-item-quantity-field"
|
|
109
110
|
/>
|
|
110
111
|
);
|
|
@@ -22,8 +22,9 @@ import Stack from '@mui/material/Stack';
|
|
|
22
22
|
import SliderLabels from './SliderLabels';
|
|
23
23
|
import SliderDisplayValue from './SliderDisplayValue';
|
|
24
24
|
import { useRendererConfigStore } from '../../../stores';
|
|
25
|
-
import {
|
|
25
|
+
import { StyledFeedbackTypography } from '../Item.styles';
|
|
26
26
|
import { StandardSlider } from './Slider.styles';
|
|
27
|
+
import AccessibleFeedback from '../ItemParts/AccessibleFeedback';
|
|
27
28
|
|
|
28
29
|
interface SliderFieldProps extends PropsWithIsTabledAttribute {
|
|
29
30
|
linkId: string;
|
|
@@ -69,6 +70,8 @@ function SliderField(props: SliderFieldProps) {
|
|
|
69
70
|
|
|
70
71
|
const hasLabels = !!(minLabel || maxLabel);
|
|
71
72
|
|
|
73
|
+
const feedbackId = itemType + '-' + linkId + '-feedback';
|
|
74
|
+
|
|
72
75
|
return (
|
|
73
76
|
<>
|
|
74
77
|
<Stack sx={{ ...sliderSx }}>
|
|
@@ -95,12 +98,17 @@ function SliderField(props: SliderFieldProps) {
|
|
|
95
98
|
disabled={readOnly && readOnlyVisualStyle === 'disabled'}
|
|
96
99
|
readOnly={readOnly && readOnlyVisualStyle === 'readonly'}
|
|
97
100
|
aria-readonly={readOnly && readOnlyVisualStyle === 'readonly'}
|
|
101
|
+
aria-describedby={feedback ? feedbackId : undefined}
|
|
98
102
|
valueLabelDisplay="auto"
|
|
99
103
|
data-test="q-item-slider-field"
|
|
100
104
|
/>
|
|
101
105
|
</Stack>
|
|
102
106
|
|
|
103
|
-
{feedback ?
|
|
107
|
+
{feedback ? (
|
|
108
|
+
<AccessibleFeedback id={feedbackId}>
|
|
109
|
+
<StyledFeedbackTypography>{feedback}</StyledFeedbackTypography>
|
|
110
|
+
</AccessibleFeedback>
|
|
111
|
+
) : null}
|
|
104
112
|
</>
|
|
105
113
|
);
|
|
106
114
|
}
|
|
@@ -25,6 +25,7 @@ import ItemRepopulateButton from '../ItemParts/ItemRepopulateButton';
|
|
|
25
25
|
import type { RenderingExtensions } from '../../../hooks/useRenderingExtensions';
|
|
26
26
|
import type { QuestionnaireItem, QuestionnaireResponseItem } from 'fhir/r4';
|
|
27
27
|
import { StandardTextField } from '../Textfield.styles';
|
|
28
|
+
import AccessibleFeedback from '../ItemParts/AccessibleFeedback';
|
|
28
29
|
|
|
29
30
|
interface StringFieldProps extends PropsWithIsTabledAttribute {
|
|
30
31
|
qItem: QuestionnaireItem;
|
|
@@ -90,7 +91,7 @@ function StringField(props: StringFieldProps) {
|
|
|
90
91
|
)
|
|
91
92
|
}
|
|
92
93
|
}}
|
|
93
|
-
helperText={feedback}
|
|
94
|
+
helperText={<AccessibleFeedback>{feedback}</AccessibleFeedback>}
|
|
94
95
|
data-test="q-item-string-field"
|
|
95
96
|
/>
|
|
96
97
|
);
|
|
@@ -23,6 +23,7 @@ import ExpressionUpdateFadingIcon from '../ItemParts/ExpressionUpdateFadingIcon'
|
|
|
23
23
|
import type { QuestionnaireItem, QuestionnaireResponseItem } from 'fhir/r4';
|
|
24
24
|
import type { RenderingExtensions } from '../../../hooks/useRenderingExtensions';
|
|
25
25
|
import ItemRepopulateButton from '../ItemParts/ItemRepopulateButton';
|
|
26
|
+
import AccessibleFeedback from '../ItemParts/AccessibleFeedback';
|
|
26
27
|
|
|
27
28
|
interface TextFieldProps {
|
|
28
29
|
qItem: QuestionnaireItem;
|
|
@@ -79,7 +80,7 @@ function TextField(props: TextFieldProps) {
|
|
|
79
80
|
)
|
|
80
81
|
}
|
|
81
82
|
}}
|
|
82
|
-
helperText={feedback}
|
|
83
|
+
helperText={<AccessibleFeedback>{feedback}</AccessibleFeedback>}
|
|
83
84
|
data-test="q-item-text-field"
|
|
84
85
|
/>
|
|
85
86
|
);
|
|
@@ -22,6 +22,7 @@ import { StandardTextField } from '../Textfield.styles';
|
|
|
22
22
|
import { useRendererConfigStore } from '../../../stores';
|
|
23
23
|
import DisplayUnitText from '../ItemParts/DisplayUnitText';
|
|
24
24
|
import { ClearButtonAdornment } from '../ItemParts/ClearButtonAdornment';
|
|
25
|
+
import AccessibleFeedback from '../ItemParts/AccessibleFeedback';
|
|
25
26
|
|
|
26
27
|
interface UrlFieldProps extends PropsWithIsTabledAttribute {
|
|
27
28
|
linkId: string;
|
|
@@ -81,7 +82,7 @@ function UrlField(props: UrlFieldProps) {
|
|
|
81
82
|
)
|
|
82
83
|
}
|
|
83
84
|
}}
|
|
84
|
-
helperText={feedback}
|
|
85
|
+
helperText={<AccessibleFeedback>{feedback}</AccessibleFeedback>}
|
|
85
86
|
data-test="q-item-url-field"
|
|
86
87
|
/>
|
|
87
88
|
);
|
|
@@ -25,6 +25,8 @@ import type { QuestionnaireItem } from 'fhir/r4';
|
|
|
25
25
|
import ContextDisplayItem from '../FormComponents/ItemParts/ContextDisplayItem';
|
|
26
26
|
import { useFocusTabHeading } from '../../hooks/useFocusTabHeading';
|
|
27
27
|
import useDisplayCqfAndCalculatedExpression from '../../hooks/useDisplayCqfAndCalculatedExpression';
|
|
28
|
+
import RequiredAsterisk from '../FormComponents/ItemParts/RequiredAsterisk';
|
|
29
|
+
import useRenderingExtensions from '../../hooks/useRenderingExtensions';
|
|
28
30
|
|
|
29
31
|
interface FormBodySingleTabProps {
|
|
30
32
|
qItem: QuestionnaireItem;
|
|
@@ -40,6 +42,10 @@ const FormBodySingleTab = memo(function FormBodySingleTab(props: FormBodySingleT
|
|
|
40
42
|
const switchTab = useQuestionnaireStore.use.switchTab();
|
|
41
43
|
const disableHeadingFocusOnTabSwitch =
|
|
42
44
|
useRendererConfigStore.use.disableHeadingFocusOnTabSwitch();
|
|
45
|
+
const requiredIndicatorPosition = useRendererConfigStore.use.requiredIndicatorPosition();
|
|
46
|
+
|
|
47
|
+
const { required } = useRenderingExtensions(qItem);
|
|
48
|
+
const readOnly = qItem.readOnly ?? false;
|
|
43
49
|
|
|
44
50
|
const focusHeading = useFocusTabHeading();
|
|
45
51
|
|
|
@@ -68,14 +74,28 @@ const FormBodySingleTab = memo(function FormBodySingleTab(props: FormBodySingleT
|
|
|
68
74
|
<ListItemText
|
|
69
75
|
primary={
|
|
70
76
|
<Box display="flex" alignItems="center" justifyContent="space-between">
|
|
71
|
-
<
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
<Box display="flex" gap={0.25}>
|
|
78
|
+
{/* Required asterisk position is in front of text */}
|
|
79
|
+
{required && requiredIndicatorPosition === 'start' ? (
|
|
80
|
+
<RequiredAsterisk>*</RequiredAsterisk>
|
|
81
|
+
) : null}
|
|
82
|
+
<Typography
|
|
83
|
+
id={`tab-${listIndex}`}
|
|
84
|
+
component="span"
|
|
85
|
+
fontWeight={600}
|
|
86
|
+
fontSize="0.8125rem"
|
|
87
|
+
aria-label={itemTextAriaLabel}>
|
|
88
|
+
{tabLabel}
|
|
89
|
+
</Typography>
|
|
90
|
+
|
|
91
|
+
{/* Required asterisk position is behind text */}
|
|
92
|
+
{required && requiredIndicatorPosition === 'end' ? (
|
|
93
|
+
<RequiredAsterisk readOnly={readOnly} variant="groupHeading">
|
|
94
|
+
*
|
|
95
|
+
</RequiredAsterisk>
|
|
96
|
+
) : null}
|
|
97
|
+
</Box>
|
|
98
|
+
|
|
79
99
|
<Box display="flex" minHeight={24} minWidth={24} ml={1}>
|
|
80
100
|
{contextDisplayItems.map((item) => {
|
|
81
101
|
return <ContextDisplayItem key={item.linkId} displayItem={item} />;
|