Input Field: Fix accessibility issues with errors not associated to proper form fields (#30430)
* Cursor first pass * Iterating * Updated snapshots * Alert role only for error or warning --------- Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
a4c745c97b
Коммит
e4124ed320
@@ -76,6 +76,7 @@ Object {
|
|||||||
class="Input_wrapper"
|
class="Input_wrapper"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
aria-invalid="false"
|
||||||
aria-label="Name"
|
aria-label="Name"
|
||||||
class="Input form-control large"
|
class="Input form-control large"
|
||||||
id="input_name"
|
id="input_name"
|
||||||
@@ -102,6 +103,8 @@ Object {
|
|||||||
class="Input_wrapper"
|
class="Input_wrapper"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
aria-describedby="error_email"
|
||||||
|
aria-invalid="true"
|
||||||
aria-label="Business Email"
|
aria-label="Business Email"
|
||||||
class="Input form-control large Input__focus"
|
class="Input form-control large Input__focus"
|
||||||
id="input_email"
|
id="input_email"
|
||||||
@@ -114,8 +117,11 @@ Object {
|
|||||||
</fieldset>
|
</fieldset>
|
||||||
<div
|
<div
|
||||||
class="Input___customMessage Input___error"
|
class="Input___customMessage Input___error"
|
||||||
|
id="error_email"
|
||||||
|
role="alert"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
|
aria-hidden="true"
|
||||||
class="icon error icon-alert-circle-outline"
|
class="icon error icon-alert-circle-outline"
|
||||||
/>
|
/>
|
||||||
<span>
|
<span>
|
||||||
@@ -136,6 +142,7 @@ Object {
|
|||||||
class="Input_wrapper"
|
class="Input_wrapper"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
aria-invalid="false"
|
||||||
aria-label="Company Name"
|
aria-label="Company Name"
|
||||||
class="Input form-control large"
|
class="Input form-control large"
|
||||||
id="input_company_name"
|
id="input_company_name"
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ exports[`components/widgets/inputs/Input should match snapshot 1`] = `
|
|||||||
className="Input_wrapper"
|
className="Input_wrapper"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
aria-invalid={false}
|
||||||
className="Input form-control medium"
|
className="Input form-control medium"
|
||||||
id="input_"
|
id="input_"
|
||||||
onBlur={[Function]}
|
onBlur={[Function]}
|
||||||
|
|||||||
@@ -80,6 +80,9 @@ const Input = React.forwardRef((
|
|||||||
const [focused, setFocused] = useState(false);
|
const [focused, setFocused] = useState(false);
|
||||||
const [customInputLabel, setCustomInputLabel] = useState<CustomMessageInputType>(null);
|
const [customInputLabel, setCustomInputLabel] = useState<CustomMessageInputType>(null);
|
||||||
|
|
||||||
|
const errorId = `error_${name || ''}`;
|
||||||
|
const inputId = `input_${name || ''}`;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (customMessage === undefined || customMessage === null) {
|
if (customMessage === undefined || customMessage === null) {
|
||||||
if (customInputLabel !== null) {
|
if (customInputLabel !== null) {
|
||||||
@@ -136,7 +139,8 @@ const Input = React.forwardRef((
|
|||||||
};
|
};
|
||||||
|
|
||||||
const showLegend = Boolean(focused || value);
|
const showLegend = Boolean(focused || value);
|
||||||
const error = customInputLabel?.type === 'error';
|
const error = customInputLabel?.type === ItemStatus.ERROR;
|
||||||
|
const warning = customInputLabel?.type === ItemStatus.WARNING;
|
||||||
const limitExceeded = limit && value && !Array.isArray(value) ? value.toString().length - limit : 0;
|
const limitExceeded = limit && value && !Array.isArray(value) ? value.toString().length - limit : 0;
|
||||||
|
|
||||||
const clearButton = value && clearable ? (
|
const clearButton = value && clearable ? (
|
||||||
@@ -161,11 +165,13 @@ const Input = React.forwardRef((
|
|||||||
return (
|
return (
|
||||||
<textarea
|
<textarea
|
||||||
ref={ref as React.RefObject<HTMLTextAreaElement>}
|
ref={ref as React.RefObject<HTMLTextAreaElement>}
|
||||||
id={`input_${name || ''}`}
|
id={inputId}
|
||||||
className={classNames('Input form-control', inputSize, inputClassName, {Input__focus: showLegend})}
|
className={classNames('Input form-control', inputSize, inputClassName, {Input__focus: showLegend})}
|
||||||
value={value}
|
value={value}
|
||||||
placeholder={placeholderValue}
|
placeholder={placeholderValue}
|
||||||
aria-label={ariaLabel}
|
aria-label={ariaLabel}
|
||||||
|
aria-describedby={error ? errorId : undefined}
|
||||||
|
aria-invalid={error || hasError || limitExceeded > 0}
|
||||||
rows={3}
|
rows={3}
|
||||||
name={name}
|
name={name}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
@@ -179,11 +185,13 @@ const Input = React.forwardRef((
|
|||||||
return (
|
return (
|
||||||
<input
|
<input
|
||||||
ref={ref as React.RefObject<HTMLInputElement>}
|
ref={ref as React.RefObject<HTMLInputElement>}
|
||||||
id={`input_${name || ''}`}
|
id={inputId}
|
||||||
className={classNames('Input form-control', inputSize, inputClassName, {Input__focus: showLegend})}
|
className={classNames('Input form-control', inputSize, inputClassName, {Input__focus: showLegend})}
|
||||||
value={value}
|
value={value}
|
||||||
placeholder={placeholderValue}
|
placeholder={placeholderValue}
|
||||||
aria-label={ariaLabel}
|
aria-label={ariaLabel}
|
||||||
|
aria-describedby={error ? errorId : undefined}
|
||||||
|
aria-invalid={error || hasError || limitExceeded > 0}
|
||||||
name={name}
|
name={name}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
@@ -223,7 +231,11 @@ const Input = React.forwardRef((
|
|||||||
{addon}
|
{addon}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
{customInputLabel && (
|
{customInputLabel && (
|
||||||
<div className={`Input___customMessage Input___${customInputLabel.type}`}>
|
<div
|
||||||
|
id={errorId}
|
||||||
|
className={`Input___customMessage Input___${customInputLabel.type}`}
|
||||||
|
role={error || warning ? 'alert' : undefined}
|
||||||
|
>
|
||||||
{customInputLabel.type && (
|
{customInputLabel.type && (
|
||||||
<i
|
<i
|
||||||
className={classNames(`icon ${customInputLabel.type}`, {
|
className={classNames(`icon ${customInputLabel.type}`, {
|
||||||
@@ -232,6 +244,7 @@ const Input = React.forwardRef((
|
|||||||
'icon-information-outline': customInputLabel.type === ItemStatus.INFO,
|
'icon-information-outline': customInputLabel.type === ItemStatus.INFO,
|
||||||
'icon-check': customInputLabel.type === ItemStatus.SUCCESS,
|
'icon-check': customInputLabel.type === ItemStatus.SUCCESS,
|
||||||
})}
|
})}
|
||||||
|
aria-hidden='true'
|
||||||
/>)}
|
/>)}
|
||||||
<span>{customInputLabel.value}</span>
|
<span>{customInputLabel.value}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -71,7 +71,10 @@ const PasswordInput = React.forwardRef((
|
|||||||
onClick={toggleShowPassword}
|
onClick={toggleShowPassword}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
>
|
>
|
||||||
<i className={showPassword && !disabled ? 'icon-eye-off-outline' : 'icon-eye-outline'}/>
|
<i
|
||||||
|
className={showPassword && !disabled ? 'icon-eye-off-outline' : 'icon-eye-outline'}
|
||||||
|
aria-hidden='true'
|
||||||
|
/>
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
value={value}
|
value={value}
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user