Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/volto/news/8033.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Announce errors via `aria-live` and expose required and invalid states on text inputs to improve accessibility of form fields. @Wagner3UB
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ exports[`Aliases > renders an aliases control component 1`] = `
value=""
/>
</div>
<div
aria-atomic="true"
aria-live="polite"
/>
</div>
</div>
</div>
Expand Down Expand Up @@ -185,6 +189,10 @@ exports[`Aliases > renders an aliases control component 1`] = `
</div>
</div>
</div>
<div
aria-atomic="true"
aria-live="polite"
/>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,13 @@ const FormFieldWrapper = ({
<>
{children}

{map(error, (message) => (
<Label key={message} basic color="red" className="form-error-label">
{message}
</Label>
))}
<div aria-live="polite" aria-atomic="true">
{map(error, (message) => (
<Label key={message} basic color="red" className="form-error-label">
{message}
</Label>
))}
</div>
</>
);

Expand Down
4 changes: 4 additions & 0 deletions packages/volto/src/components/manage/Widgets/TextWidget.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ const TextWidget = (props) => {
placeholder,
isDisabled,
focus,
required,
error,
} = props;

const ref = useRef();
Expand Down Expand Up @@ -49,6 +51,8 @@ const TextWidget = (props) => {
onClick={() => onClick()}
minLength={minLength || null}
maxLength={maxLength || null}
aria-required={required ? 'true' : undefined}
aria-invalid={error?.length > 0 ? 'true' : undefined}
/>
{icon && iconAction && (
<button className={`field-${id}-action-button`} onClick={iconAction}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ exports[`AlignWidget > renders with custom actions 1`] = `
</svg>
</label>
</div>
<div
aria-atomic="true"
aria-live="polite"
/>
</div>
</div>
</div>
Expand Down Expand Up @@ -306,6 +310,10 @@ exports[`AlignWidget > renders with default actions 1`] = `
</svg>
</label>
</div>
<div
aria-atomic="true"
aria-live="polite"
/>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,10 @@ exports[`No 'No value' option when default value is 0 1`] = `
</div>
</div>
</div>
<div
aria-atomic="true"
aria-live="polite"
/>
</div>
</div>
</div>
Expand Down Expand Up @@ -320,6 +324,10 @@ exports[`renders an array widget component 1`] = `
</div>
</div>
</div>
<div
aria-atomic="true"
aria-live="polite"
/>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ exports[`ButtonsWidget > renders actions info provided via props 1`] = `
</svg>
</label>
</div>
<div
aria-atomic="true"
aria-live="polite"
/>
</div>
</div>
</div>
Expand Down Expand Up @@ -282,6 +286,10 @@ exports[`ButtonsWidget > renders string-based actions 1`] = `
</svg>
</label>
</div>
<div
aria-atomic="true"
aria-live="polite"
/>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,12 @@ exports[`renders a checkbox group widget component 1`] = `
</div>
<div
class="eight wide column"
/>
>
<div
aria-atomic="true"
aria-live="polite"
/>
</div>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ exports[`CheckboxWidget > renders a checkbox widget component 1`] = `
</label>
</div>
</div>
<div
aria-atomic="true"
aria-live="polite"
/>
</div>
</div>
</div>
Expand Down Expand Up @@ -80,6 +84,10 @@ exports[`CheckboxWidget > renders a checkbox widget component checked 1`] = `
</label>
</div>
</div>
<div
aria-atomic="true"
aria-live="polite"
/>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ exports[`datetime widget converts UTC date and adapts to local datetime 1`] = `
</svg>
</button>
</div>
<div
aria-atomic="true"
aria-live="polite"
/>
</div>
</div>
</div>
Expand Down Expand Up @@ -211,6 +215,10 @@ exports[`renders a datetime widget component 1`] = `
</svg>
</button>
</div>
<div
aria-atomic="true"
aria-live="polite"
/>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ exports[`renders an email widget component 1`] = `
value=""
/>
</div>
<div
aria-atomic="true"
aria-live="polite"
/>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ exports[`IdWidget > renders an empty id widget component 1`] = `
value=""
/>
</div>
<div
aria-atomic="true"
aria-live="polite"
/>
</div>
</div>
</div>
Expand Down Expand Up @@ -83,6 +87,10 @@ exports[`IdWidget > renders an id widget with a valid dot character 1`] = `
value="test-id"
/>
</div>
<div
aria-atomic="true"
aria-live="polite"
/>
</div>
</div>
</div>
Expand Down Expand Up @@ -128,6 +136,10 @@ exports[`IdWidget > renders an id widget with a valid value 1`] = `
value="test-id"
/>
</div>
<div
aria-atomic="true"
aria-live="polite"
/>
</div>
</div>
</div>
Expand Down Expand Up @@ -174,9 +186,14 @@ exports[`IdWidget > renders an id widget with an reserved name 1`] = `
/>
</div>
<div
class="ui red basic label form-error-label"
aria-atomic="true"
aria-live="polite"
>
This is a reserved name and can't be used
<div
class="ui red basic label form-error-label"
>
This is a reserved name and can't be used
</div>
</div>
</div>
</div>
Expand Down Expand Up @@ -224,9 +241,14 @@ exports[`IdWidget > renders an id widget with invalid characters 1`] = `
/>
</div>
<div
class="ui red basic label form-error-label"
aria-atomic="true"
aria-live="polite"
>
Only 7-bit bytes characters are allowed. Cannot contain uppercase letters, special characters: &lt;, &gt;, &, #, /, ?, or others that are illegal in URLs. Cannot start with: _, aq_, @@, ++. Cannot end with __. Cannot be: request,contributors, ., .., "". Cannot contain new lines.
<div
class="ui red basic label form-error-label"
>
Only 7-bit bytes characters are allowed. Cannot contain uppercase letters, special characters: &lt;, &gt;, &, #, /, ?, or others that are illegal in URLs. Cannot start with: _, aq_, @@, ++. Cannot end with __. Cannot be: request,contributors, ., .., "". Cannot contain new lines.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just noticed this. Does the ampersand render to the screen, or does it need to be encoded?

Copy link
Contributor

@pnicolli pnicolli Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This good question got me curious and I checked, since this is a snapshot test result and should (theoretically) represent the result of rendering.
This is the actual string that is being render, the &lt; was converted while the & was not, so I think it's safe to assume this is fine, at least for the sake of this PR.

https://github.com/plone/volto/blob/main/packages/volto/src/components/manage/Widgets/IdWidget.jsx#L30
https://github.com/plone/volto/blob/main/packages/volto/locales/en/LC_MESSAGES/volto.po#L2675

</div>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ exports[`renders an image sizes widget component 1`] = `
</div>
</div>
</div>
<div
aria-atomic="true"
aria-live="polite"
/>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ exports[`renders a number widget component 1`] = `
value=""
/>
</div>
<div
aria-atomic="true"
aria-live="polite"
/>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ exports[`renders a objectBrowser widget component 1`] = `
</svg>
</button>
</div>
<div
aria-atomic="true"
aria-live="polite"
/>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ exports[`renders an object list widget component 1`] = `
 Add Link
</button>
</div>
<div
aria-atomic="true"
aria-live="polite"
/>
</div>
</div>
<div
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ exports[`renders a password widget component 1`] = `
value=""
/>
</div>
<div
aria-atomic="true"
aria-live="polite"
/>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,12 @@ exports[`renders a radio group widget component 1`] = `
</div>
<div
class="eight wide column"
/>
>
<div
aria-atomic="true"
aria-live="polite"
/>
</div>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ exports[`renders an array widget component 1`] = `
</div>
</div>
</div>
<div
aria-atomic="true"
aria-live="polite"
/>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ exports[`RegistryImageWidget > renders a file widget component with value 1`] =
</svg>
</button>
</div>
<div
aria-atomic="true"
aria-live="polite"
/>
</div>
</div>
</div>
Expand Down Expand Up @@ -146,6 +150,10 @@ exports[`RegistryImageWidget > renders an empty file widget component 1`] = `
<div
class="field-file-name"
/>
<div
aria-atomic="true"
aria-live="polite"
/>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ exports[`renders a select widget component 1`] = `
</div>
</div>
</div>
<div
aria-atomic="true"
aria-live="polite"
/>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,10 @@ exports[`No 'No value' option when default value is 0 1`] = `
value="0"
/>
</div>
<div
aria-atomic="true"
aria-live="polite"
/>
</div>
</div>
</div>
Expand Down Expand Up @@ -304,6 +308,10 @@ exports[`renders a select widget component 1`] = `
value=""
/>
</div>
<div
aria-atomic="true"
aria-live="polite"
/>
</div>
</div>
</div>
Expand Down
Loading
Loading