Commit 49ff559
Issue/1640 (aehrc#1830)
* feat: Associate instruction text with input fields using aria-describedby (aehrc#1640)
This commit implements accessibility improvements by associating instructional
text with their parent form fields using the aria-describedby attribute.
Changes:
- Modified ItemFieldGrid to recursively add aria-describedby to input/textarea
elements and radio/checkbox groups when instructions are present
- Added getInstructionsId helper function to generate instruction IDs
- Updated DisplayInstructions component to accept an optional id prop
- Added Storybook tests for String, Boolean, and Text items to verify
aria-describedby functionality with instruction text
Screen readers will now announce instructional text when a field receives focus,
improving the accessibility experience for users with visual impairments.
Fixes aehrc#1640
* feat: Associate instruction text with input fields using aria-describedby
- Apply aria-describedby directly to MUI components via their props
- For text fields (String, Text): Use slotProps.input to add aria-describedby
- For radio buttons (Boolean): Use inputProps on individual radio inputs
- Updated ChoiceRadioSingle to accept and forward ariaDescribedBy prop
- Removed dynamic React element cloning from ItemFieldGrid
- Consolidated accessibility tests in Testing/Accessibility/Instructions
- Fixed automated Storybook tests for accessibility verification
- All three item types (String, Text, Boolean) properly announce instructions via VoiceOver
Fixes aehrc#1640
* fix: Remove unused 'within' import and rebase on main
- Fixed lint error blocking CI/CD
- Rebased issue/1640 on latest main
- Verified aria-describedby with VoiceOver testing
- Instructions announced correctly when field gets focus
Fixes aehrc#1640
* fix: Implement aria-describedby for accessibility instructions (Issue aehrc#1640)
Address reviewer feedback for Issue aehrc#1640 by implementing proper aria-describedby
support and adding comprehensive VoiceOver accessibility tests.
## Changes Made:
### Bug Fixes (from reviewer feedback):
1. **Fixed slotProps placement**: Changed from slotProps.input to slotProps.htmlInput
in StringField and TextField components to correctly apply aria-describedby to
the actual input/textarea elements instead of wrapper divs
2. **Fixed test selector**: Updated StringInstructionsAccessibility story to query
for 'input' instead of 'textarea' for string fields
3. **Removed unnecessary type**: Removed 'aria-describedby'?: string type from
Radio.styles.tsx as the attribute is passed via inputProps
### Feature Implementation:
4. **Added aria-describedby support** to the following item types:
- Integer (IntegerField & IntegerItem)
- Decimal (DecimalField & DecimalItem)
- DateTime (DateTimeField, CustomDateTimeItem, CustomDateField, CustomDateItem)
- String (already working, fixed slotProps)
- Text (already working, fixed slotProps)
- Boolean (already working)
Pattern: Each field component now accepts instructionsId prop and applies it
via slotProps.htmlInput with aria-describedby attribute
### Test Coverage:
5. **Added VoiceOver accessibility test stories** for all item types:
- IntegerInstructionsAccessibility
- DecimalInstructionsAccessibility
- QuantityInstructionsAccessibility
- UrlInstructionsAccessibility
- AttachmentInstructionsAccessibility
- DateInstructionsAccessibility
- DateTimeInstructionsAccessibility
- TimeInstructionsAccessibility
- ChoiceInstructionsAccessibility
- OpenChoiceInstructionsAccessibility
## Testing:
- ✅ IntegerInstructionsAccessibility - VoiceOver confirmed working
- ✅ DecimalInstructionsAccessibility - VoiceOver confirmed working
- ✅ DateTimeInstructionsAccessibility - VoiceOver confirmed working
- ✅ BooleanInstructionsAccessibility - VoiceOver confirmed working
- ✅ StringInstructionsAccessibility - Fixed and working
- ✅ TextInstructionsAccessibility - Fixed and working
## Remaining Work (for future commits):
The following item types still need aria-describedby implementation:
- Date (has error to fix)
- Quantity
- Time
- URL
- Attachment
- Choice
- Open-Choice
These follow the same pattern and will be addressed in subsequent commits.
Refs aehrc#1640
Made-with: Cursor
* fix: Add aria-describedby support for URL and Quantity item types (Issue aehrc#1640)
Implements accessibility instructions for URL and Quantity fields by adding instructionsId prop support. For Quantity items, aria-describedby is applied to both the value input field and unit selector to ensure instructions are announced regardless of which field receives focus first.
Made-with: Cursor
* fix: Move instructionsId generation after feedback in CustomDateItem (Issue aehrc#1640)
Fixes 'Cannot access feedback before initialization' error by ensuring feedback is defined before being used to generate instructionsId.
* feat: Implement CustomTimeItem and CustomChoiceSelectField for accessibility (Issue aehrc#1640)
Problem:
MUI TimePicker and Autocomplete components have complex internal structures that
prevent aria-describedby from being properly announced by VoiceOver.
- TimePicker uses segmented inputs (separate spans for hours, minutes, AM/PM)
- Autocomplete has nested internal elements (button + hidden input + listbox)
Solution:
1. CustomTimeItem (Time field)
- Created CustomTimeItem.tsx using existing CustomTimeField component
- CustomTimeField uses standard TextField + MUI Select dropdown (not TimePicker)
- Matches pattern from CustomDateItem (which works successfully)
- Added instructionsId prop support to CustomTimeField
- Updated SingleItemSwitcher to use CustomTimeItem instead of TimeItem
2. CustomChoiceSelectField (Choice Select dropdown)
- Created CustomChoiceSelectField.tsx using standard MUI Select (not Autocomplete)
- Applied aria-describedby and aria-labelledby directly to Select component
- MUI Select passes these attributes through to internal focusable button element
- Added useRenderingExtensions and getInstructionsId to ChoiceSelectAnswerOptionItem
- Updated ChoiceSelectAnswerOptionView to use CustomChoiceSelectField
3. Supporting Changes
- Added instructionsId support to CustomTimeField for DateTime items
- Updated DateTimeField to pass instructionsId to CustomTimeField
- Added instructionsId prop threading for Choice Radio fields
Testing:
Both implementations successfully tested with VoiceOver.
Related to: aehrc#1640
Made-with: Cursor
* feat: Add aria-describedby support for Attachment fields (Issue aehrc#1640)
Problem:
Attachment fields have multiple focusable elements (file upload button, URL field,
filename field) but VoiceOver was not reading the accessibility instructions when
users navigated to the field.
Solution:
Applied aria-describedby directly to the file upload button (first focusable element),
following the same successful pattern used for Choice Select fields where MUI
components need aria attributes on the actual interactive element, not wrapper groups.
Changes:
- AttachmentItem: Added useRenderingExtensions and getInstructionsId to generate
instructionsId from rendering extensions
- AttachmentFieldWrapper: Added instructionsId and itemText props, passed to
AttachmentField
- AttachmentField: Added role="group" wrapper with aria-labelledby/aria-describedby
and passed instructionsId to AttachmentFileCollector
- AttachmentFileCollector: Applied aria-describedby to the IconButton (Attach file)
- Improved UX: Made explanatory text smaller, lighter, and italic to distinguish
from user-defined instructions
Testing:
VoiceOver successfully reads the instructions when focusing the Attach file button.
Related to: aehrc#1640
Made-with: Cursor
* WIP: Add aria-describedby support to Open-Choice Autocomplete (not working yet)
Problem:
- Open-Choice Autocomplete fields do not announce instructions to screen readers
- VoiceOver only reads "Occupation, list box popup collapsed, edit text" without the instructions
Attempted solution:
- Added useRenderingExtensions and getInstructionsId to OpenChoiceAutocompleteItem
- Passed instructionsId down to OpenChoiceAutocompleteField
- Attempted to apply aria-describedby via slotProps.input.inputProps
Current status:
- aria-describedby is NOT being applied to the textarea element
- MUI Autocomplete has complex internal structure that does not properly propagate aria attributes
- Similar issue to Time and Choice fields that required custom implementations
Next steps:
- Create CustomOpenChoiceField.tsx component (similar to CustomTimeItem and CustomChoiceSelectField)
- Use standard MUI Select + TextField to support both predefined options and custom text input
- This will properly support aria-describedby for accessibility
Files modified:
- OpenChoiceAutocompleteItem.tsx: Added instructionsId generation
- OpenChoiceAutocompleteField.tsx: Attempted aria-describedby implementation (incomplete)
Related issue: aehrc#1640
Made-with: Cursor
* feat: Add aria-describedby support to Open-Choice fields using CustomOpenChoiceField
Problem:
- Open-Choice fields did not announce instructions to screen readers
- MUI Autocomplete has complex internal structure that does not propagate aria-describedby
- VoiceOver only read field name without instructions
Solution:
Created CustomOpenChoiceField component following Time and Choice patterns:
- Uses MUI Select dropdown for predefined Coding options
- Includes custom value option that reveals TextField for custom text input
- Properly applies aria-describedby to both Select and TextField elements
Changes:
1. Created CustomOpenChoiceField.tsx with Select + TextField
2. Updated OpenChoiceAutocompleteItem.tsx to use CustomOpenChoiceField
3. Updated OpenChoiceSelectAnswerOptionItem.tsx to use CustomOpenChoiceField
4. Updated AccessibilityInstructions.stories.tsx test for new component
Testing:
- VoiceOver correctly reads instructions for Open-Choice dropdown
- VoiceOver reads instructions for custom text input field
- All functionality working as expected
Related issue: aehrc#1640
Made-with: Cursor
* fix: stabilize storybook CI interactions and accessibility story selectors
Align story test utilities and selectors with current renderer DOM behavior, remove lint-blocking issues, and harden Storybook CI startup/timeout settings to avoid flaky connection and interaction failures.
Made-with: Cursor
* fix: restore renderer controls and normalize VoiceOver instructions
Revert unintended custom choice/open-choice/time control swaps and align instruction announcements across control variants for consistent VoiceOver behavior in storybook and 715 testing.
Made-with: Cursor
* style: apply prettier to choice and slider components
* fix(renderer): stabilize Storybook tests and a11y for slider and time
- Expose aria-describedby on slider for instructions in tests\n- Time/choice/slider-related story plays and testUtils (MUI TimePicker via clock)\n- AccessibilityInstructions and Time stories; package.json CI test timeout if changed
* fix(renderer): resolve Prettier lint errors in Storybook test utilities
Apply eslint --fix formatting in testUtils and AccessibilityInstructions
stories so CI lint passes.
Made-with: Cursor
* fix(renderer): announce slider instructions via MUI input slot
Pass aria-describedby through slotProps.input so screen readers use the
focused thumb input. Align SliderItem with IntegerItem using
useRenderingExtensions and ItemFieldGrid feedback.
---------
Co-authored-by: Maryam Mehdizadeh <maryam.mehdizadeh@csiro.au>1 parent 9ddc3de commit 49ff559
72 files changed
Lines changed: 1779 additions & 292 deletions
File tree
- packages/smart-forms-renderer
- .storybook
- src
- components/FormComponents
- AttachmentItem
- BooleanItem
- ChoiceItems
- DateTimeItems
- CustomDateItem
- CustomDateTimeItem
- utils
- DecimalItem
- DisplayItem
- IntegerItem
- ItemParts
- OpenChoiceItems
- QuantityItem
- SliderItem
- StringItem
- TextItem
- TimeItem
- UrlItem
- stories
- itemTypes
- testing
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
5 | | - | |
| 5 | + | |
6 | 6 | | |
7 | 7 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
12 | | - | |
| 12 | + | |
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
| |||
Lines changed: 77 additions & 61 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
32 | 32 | | |
33 | 33 | | |
34 | 34 | | |
| 35 | + | |
35 | 36 | | |
36 | 37 | | |
37 | 38 | | |
| 39 | + | |
38 | 40 | | |
39 | 41 | | |
40 | 42 | | |
| |||
44 | 46 | | |
45 | 47 | | |
46 | 48 | | |
| 49 | + | |
47 | 50 | | |
48 | 51 | | |
49 | 52 | | |
50 | 53 | | |
| 54 | + | |
51 | 55 | | |
52 | 56 | | |
53 | 57 | | |
| |||
64 | 68 | | |
65 | 69 | | |
66 | 70 | | |
67 | | - | |
68 | | - | |
69 | | - | |
70 | | - | |
71 | | - | |
72 | | - | |
73 | | - | |
74 | | - | |
75 | | - | |
76 | | - | |
77 | | - | |
78 | | - | |
79 | | - | |
80 | | - | |
81 | | - | |
82 | | - | |
83 | | - | |
84 | | - | |
85 | | - | |
86 | | - | |
87 | | - | |
88 | | - | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
89 | 77 | | |
90 | 78 | | |
91 | | - | |
92 | | - | |
93 | | - | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
94 | 83 | | |
95 | | - | |
96 | | - | |
97 | | - | |
98 | | - | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
99 | 98 | | |
100 | | - | |
101 | | - | |
102 | | - | |
103 | | - | |
104 | | - | |
105 | | - | |
106 | | - | |
107 | | - | |
108 | | - | |
109 | | - | |
110 | | - | |
111 | | - | |
112 | | - | |
113 | | - | |
114 | | - | |
115 | | - | |
116 | | - | |
117 | | - | |
118 | | - | |
119 | | - | |
120 | | - | |
121 | | - | |
122 | | - | |
123 | | - | |
124 | | - | |
| 99 | + | |
125 | 100 | | |
126 | | - | |
127 | 101 | | |
128 | | - | |
129 | | - | |
130 | | - | |
131 | | - | |
132 | | - | |
133 | | - | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
134 | 150 | | |
135 | 151 | | |
136 | 152 | | |
| |||
Lines changed: 6 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
35 | 35 | | |
36 | 36 | | |
37 | 37 | | |
| 38 | + | |
38 | 39 | | |
39 | 40 | | |
40 | 41 | | |
| |||
48 | 49 | | |
49 | 50 | | |
50 | 51 | | |
| 52 | + | |
51 | 53 | | |
52 | 54 | | |
53 | 55 | | |
| |||
60 | 62 | | |
61 | 63 | | |
62 | 64 | | |
| 65 | + | |
63 | 66 | | |
64 | 67 | | |
65 | 68 | | |
66 | 69 | | |
| 70 | + | |
67 | 71 | | |
68 | 72 | | |
69 | 73 | | |
| |||
85 | 89 | | |
86 | 90 | | |
87 | 91 | | |
| 92 | + | |
88 | 93 | | |
89 | 94 | | |
90 | 95 | | |
91 | 96 | | |
| 97 | + | |
92 | 98 | | |
93 | 99 | | |
94 | 100 | | |
| |||
Lines changed: 15 additions & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
| 29 | + | |
29 | 30 | | |
30 | 31 | | |
31 | 32 | | |
32 | 33 | | |
33 | 34 | | |
34 | 35 | | |
35 | | - | |
| 36 | + | |
36 | 37 | | |
37 | 38 | | |
38 | 39 | | |
| |||
71 | 72 | | |
72 | 73 | | |
73 | 74 | | |
74 | | - | |
| 75 | + | |
75 | 76 | | |
76 | | - | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
77 | 83 | | |
78 | | - | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
79 | 90 | | |
80 | 91 | | |
81 | 92 | | |
| |||
Lines changed: 7 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
21 | 21 | | |
22 | 22 | | |
23 | 23 | | |
| 24 | + | |
24 | 25 | | |
25 | 26 | | |
26 | 27 | | |
27 | 28 | | |
28 | 29 | | |
| 30 | + | |
29 | 31 | | |
30 | 32 | | |
31 | 33 | | |
| |||
60 | 62 | | |
61 | 63 | | |
62 | 64 | | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
63 | 69 | | |
64 | 70 | | |
65 | 71 | | |
| |||
111 | 117 | | |
112 | 118 | | |
113 | 119 | | |
| 120 | + | |
114 | 121 | | |
115 | 122 | | |
116 | 123 | | |
| |||
Lines changed: 15 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
38 | 38 | | |
39 | 39 | | |
40 | 40 | | |
| 41 | + | |
41 | 42 | | |
42 | 43 | | |
43 | 44 | | |
44 | 45 | | |
45 | 46 | | |
46 | | - | |
47 | | - | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
48 | 57 | | |
49 | 58 | | |
50 | 59 | | |
| |||
80 | 89 | | |
81 | 90 | | |
82 | 91 | | |
| 92 | + | |
83 | 93 | | |
84 | 94 | | |
85 | 95 | | |
| |||
105 | 115 | | |
106 | 116 | | |
107 | 117 | | |
108 | | - | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
109 | 121 | | |
110 | 122 | | |
111 | 123 | | |
| |||
Lines changed: 9 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
25 | | - | |
| 25 | + | |
26 | 26 | | |
27 | 27 | | |
| 28 | + | |
28 | 29 | | |
29 | 30 | | |
30 | 31 | | |
| |||
45 | 46 | | |
46 | 47 | | |
47 | 48 | | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
48 | 53 | | |
49 | 54 | | |
50 | 55 | | |
| |||
86 | 91 | | |
87 | 92 | | |
88 | 93 | | |
| 94 | + | |
89 | 95 | | |
90 | 96 | | |
91 | 97 | | |
| |||
101 | 107 | | |
102 | 108 | | |
103 | 109 | | |
| 110 | + | |
104 | 111 | | |
105 | 112 | | |
106 | 113 | | |
| |||
124 | 131 | | |
125 | 132 | | |
126 | 133 | | |
| 134 | + | |
127 | 135 | | |
128 | 136 | | |
129 | 137 | | |
| |||
0 commit comments