Skip to content

Commit

Permalink
Add screen reader announcement of errors to *Field components.
Browse files Browse the repository at this point in the history
  • Loading branch information
plundaahl-bold committed Nov 7, 2022
1 parent cda7814 commit 519b7c2
Show file tree
Hide file tree
Showing 10 changed files with 201 additions and 76 deletions.
10 changes: 9 additions & 1 deletion src/components/checkboxfield/CheckboxField.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,32 @@ class CheckboxField extends Component {
constructor(props) {
super(props);
this.forId = `stxField${generateUniqueId()}`;
this.messageId = `stxFieldMessage${generateUniqueId()}`;
}

render() {
const { id, className, helpText, messageType, messageText, label, ...remainingProps } = this.props;
const { id, className, helpText, messageType, messageText, messageId, label, ...remainingProps } = this.props;
const classNames = cn([
'stx-checkbox-field__field',
className,
]);

const hasValidationError = messageType === 'alert' || messageType === 'error';

return (
<Field
className={classNames}
helpText={helpText}
messageType={messageType}
messageText={messageText}
messageId={this.messageId}
>
<div className='stx-checkbox-field__div'>
<Checkbox
id={id || this.forId}
className='stx-checkbox-field__checkbox'
aria-invalid={hasValidationError ? 'true' : undefined}
aria-describedby={messageId || this.messageId}
{...remainingProps}
/>

Expand All @@ -58,6 +64,8 @@ CheckboxField.propTypes = {
messageText: PropTypes.string,
/** The type of message to display */
messageType: PropTypes.oneOf(['alert', 'warning', 'success', '']),
/** The ID of the associated message element (automatically generated if not provided) */
messageId: PropTypes.oneOf([PropTypes.string, PropTypes.undefined]),
/** Whether or not the checkbox is disabled */
disabled: PropTypes.bool,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,20 @@
exports[`CheckboxField CheckboxField render additional props 1`] = `
<Field
className="stx-checkbox-field__field"
messageId="stxFieldMessage18"
>
<div
className="stx-checkbox-field__div"
>
<Checkbox
aria-describedby="stxFieldMessage18"
className="stx-checkbox-field__checkbox"
data-additional-prop="additional-prop"
id="stxField9"
id="stxField17"
/>
<label
className="stx-checkbox-field__label"
htmlFor="stxField9"
htmlFor="stxField17"
/>
</div>
</Field>
Expand All @@ -23,18 +25,20 @@ exports[`CheckboxField CheckboxField render additional props 1`] = `
exports[`CheckboxField CheckboxField render disabled when disabled is true 1`] = `
<Field
className="stx-checkbox-field__field"
messageId="stxFieldMessage16"
>
<div
className="stx-checkbox-field__div"
>
<Checkbox
aria-describedby="stxFieldMessage16"
className="stx-checkbox-field__checkbox"
disabled={true}
id="stxField8"
id="stxField15"
/>
<label
className="stx-checkbox-field__label"
htmlFor="stxField8"
htmlFor="stxField15"
/>
</div>
</Field>
Expand All @@ -43,18 +47,20 @@ exports[`CheckboxField CheckboxField render disabled when disabled is true 1`] =
exports[`CheckboxField CheckboxField renders checked 1`] = `
<Field
className="stx-checkbox-field__field"
messageId="stxFieldMessage6"
>
<div
className="stx-checkbox-field__div"
>
<Checkbox
aria-describedby="stxFieldMessage6"
checked={true}
className="stx-checkbox-field__checkbox"
id="stxField3"
id="stxField5"
/>
<label
className="stx-checkbox-field__label"
htmlFor="stxField3"
htmlFor="stxField5"
/>
</div>
</Field>
Expand All @@ -63,19 +69,21 @@ exports[`CheckboxField CheckboxField renders checked 1`] = `
exports[`CheckboxField CheckboxField renders with a success messageText 1`] = `
<Field
className="stx-checkbox-field__field"
messageId="stxFieldMessage10"
messageText="message-text"
messageType="success"
>
<div
className="stx-checkbox-field__div"
>
<Checkbox
aria-describedby="stxFieldMessage10"
className="stx-checkbox-field__checkbox"
id="stxField5"
id="stxField9"
/>
<label
className="stx-checkbox-field__label"
htmlFor="stxField5"
htmlFor="stxField9"
/>
</div>
</Field>
Expand All @@ -84,19 +92,21 @@ exports[`CheckboxField CheckboxField renders with a success messageText 1`] = `
exports[`CheckboxField CheckboxField renders with a warning messageText 1`] = `
<Field
className="stx-checkbox-field__field"
messageId="stxFieldMessage12"
messageText="message-text"
messageType="warning"
>
<div
className="stx-checkbox-field__div"
>
<Checkbox
aria-describedby="stxFieldMessage12"
className="stx-checkbox-field__checkbox"
id="stxField6"
id="stxField11"
/>
<label
className="stx-checkbox-field__label"
htmlFor="stxField6"
htmlFor="stxField11"
/>
</div>
</Field>
Expand All @@ -105,19 +115,22 @@ exports[`CheckboxField CheckboxField renders with a warning messageText 1`] = `
exports[`CheckboxField CheckboxField renders with an alert messageText 1`] = `
<Field
className="stx-checkbox-field__field"
messageId="stxFieldMessage14"
messageText="message-text"
messageType="alert"
>
<div
className="stx-checkbox-field__div"
>
<Checkbox
aria-describedby="stxFieldMessage14"
aria-invalid="true"
className="stx-checkbox-field__checkbox"
id="stxField7"
id="stxField13"
/>
<label
className="stx-checkbox-field__label"
htmlFor="stxField7"
htmlFor="stxField13"
/>
</div>
</Field>
Expand All @@ -126,11 +139,13 @@ exports[`CheckboxField CheckboxField renders with an alert messageText 1`] = `
exports[`CheckboxField CheckboxField renders with basic props 1`] = `
<Field
className="stx-checkbox-field__field html-class"
messageId="stxFieldMessage2"
>
<div
className="stx-checkbox-field__div"
>
<Checkbox
aria-describedby="stxFieldMessage2"
className="stx-checkbox-field__checkbox"
id="test-id"
name="name"
Expand All @@ -152,17 +167,19 @@ exports[`CheckboxField CheckboxField renders with helpText 1`] = `
<Field
className="stx-checkbox-field__field"
helpText="help-text"
messageId="stxFieldMessage8"
>
<div
className="stx-checkbox-field__div"
>
<Checkbox
aria-describedby="stxFieldMessage8"
className="stx-checkbox-field__checkbox"
id="stxField4"
id="stxField7"
/>
<label
className="stx-checkbox-field__label"
htmlFor="stxField4"
htmlFor="stxField7"
/>
</div>
</Field>
Expand All @@ -171,17 +188,19 @@ exports[`CheckboxField CheckboxField renders with helpText 1`] = `
exports[`CheckboxField CheckboxField renders without any props 1`] = `
<Field
className="stx-checkbox-field__field"
messageId="stxFieldMessage4"
>
<div
className="stx-checkbox-field__div"
>
<Checkbox
aria-describedby="stxFieldMessage4"
className="stx-checkbox-field__checkbox"
id="stxField2"
id="stxField3"
/>
<label
className="stx-checkbox-field__label"
htmlFor="stxField2"
htmlFor="stxField3"
/>
</div>
</Field>
Expand Down
7 changes: 5 additions & 2 deletions src/components/field/Field.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import './Field.css';

function Field(props) {
const {
children, className, disabled, helpText, htmlFor, id, label, messageText, messageType, ariaLive, ...otherProps
children, className, disabled, helpText, htmlFor, id, messageId, label, messageText, messageType, ariaLive, ...otherProps
} = props;

const classNames = cn([
Expand All @@ -19,7 +19,7 @@ function Field(props) {
]);

const renderMessageText = () => {
return messageText && <div className='stx-field__message' aria-live={ariaLive}>{ messageText }</div>;
return messageText && <div className='stx-field__message' aria-live={ariaLive} id={messageId}>{ messageText }</div>;
};

const renderHelpText = () => {
Expand Down Expand Up @@ -73,6 +73,9 @@ Field.propTypes = {
/** The text to display for the message */
messageText: PropTypes.string,

/** A unique ID for the message element */
messageId: PropTypes.string,

/** Child elements */
children: PropTypes.node,
};
Expand Down
10 changes: 9 additions & 1 deletion src/components/inputfield/InputField.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,18 @@ class InputField extends Component {
constructor(props) {
super(props);
this.forId = `stxField${generateUniqueId()}`;
this.messageId = `stxFieldMessage${generateUniqueId()}`;
}

render() {
const { className, disabled, helpText, id, label, messageText, messageType, readOnly, ariaLive, ...otherProps } = this.props;
const { className, disabled, helpText, id, label, messageText, messageType, messageId, readOnly, ariaLive, ...otherProps } = this.props;
const classNames = cn([
'stx-field--with-input',
className,
]);

const hasValidationError = messageType === 'alert' || messageType === 'error';

return (
<Field
label={label}
Expand All @@ -29,6 +32,7 @@ class InputField extends Component {
className={classNames}
messageType={messageType}
messageText={messageText}
messageId={messageId || this.messageId}
disabled={disabled}
readOnly={readOnly}
ariaLive={ariaLive}
Expand All @@ -39,6 +43,8 @@ class InputField extends Component {
messageType={messageType}
disabled={disabled}
readOnly={readOnly}
aria-invalid={hasValidationError ? 'true' : undefined}
aria-describedby={messageId || this.messageId}
/>
</Field>
);
Expand All @@ -59,6 +65,8 @@ InputField.propTypes = {
messageType: PropTypes.oneOf(['success', 'warning', 'alert', '']),
/** The text that should appear as a message */
messageText: PropTypes.string,
/** The ID of the associated message element */
messageId: PropTypes.oneOf([PropTypes.string, PropTypes.undefined]),
/** The aria-live attribute value that will be set on the message if one is given */
ariaLive: PropTypes.oneOf(['assertive', 'polite']),
/** Whether or not the field is disabled */
Expand Down
Loading

0 comments on commit 519b7c2

Please sign in to comment.