mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-12-22 16:29:51 +01:00
Improve accessibility (#1956)
Fixes several accessibility issues: - Provide a style for empty table column headers - Add aria references and aria-labels - Remove aria references if the referenced element is not rendered
This commit is contained in:
committed by
GitHub
parent
9fa0396167
commit
1fe7b0a01e
@@ -37,6 +37,7 @@ type Props = {
|
||||
label?: string;
|
||||
testId?: string;
|
||||
searchPlaceholder?: string;
|
||||
groupAriaLabelledby?: string;
|
||||
};
|
||||
|
||||
const createAbsoluteLink = (url: string) => {
|
||||
@@ -52,7 +53,8 @@ const OverviewPageActions: FC<Props> = ({
|
||||
groupSelected,
|
||||
label,
|
||||
testId,
|
||||
searchPlaceholder
|
||||
searchPlaceholder,
|
||||
groupAriaLabelledby
|
||||
}) => {
|
||||
const history = useHistory();
|
||||
const location = useLocation();
|
||||
@@ -62,6 +64,7 @@ const OverviewPageActions: FC<Props> = ({
|
||||
const groupSelector = groups && (
|
||||
<div className="column is-flex">
|
||||
<Select
|
||||
ariaLabelledby={groupAriaLabelledby}
|
||||
className="is-fullwidth"
|
||||
options={groups.map(g => ({ value: g, label: g }))}
|
||||
value={currentGroup}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
import React, { FC } from "react";
|
||||
import classNames from "classnames";
|
||||
import { Color, Size } from "./styleConstants";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
@@ -51,8 +52,10 @@ const Tag: FC<Props> = ({
|
||||
title,
|
||||
onClick,
|
||||
onRemove,
|
||||
children,
|
||||
children
|
||||
}) => {
|
||||
const [t] = useTranslation("commons");
|
||||
|
||||
let showIcon = null;
|
||||
if (icon) {
|
||||
showIcon = (
|
||||
@@ -64,7 +67,7 @@ const Tag: FC<Props> = ({
|
||||
}
|
||||
let showDelete = null;
|
||||
if (onRemove) {
|
||||
showDelete = <button className="tag is-delete" onClick={onRemove} />;
|
||||
showDelete = <button className="tag is-delete" onClick={onRemove} aria-label={t("tag.delete")} />;
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -78,7 +81,7 @@ const Tag: FC<Props> = ({
|
||||
{
|
||||
"is-outlined": outlined,
|
||||
"is-rounded": rounded,
|
||||
"is-clickable": onClick,
|
||||
"is-clickable": onClick
|
||||
},
|
||||
size === "small" && smallClassNames
|
||||
)}
|
||||
|
||||
@@ -34,6 +34,7 @@ exports[`Storyshots BranchSelector Default 1`] = `
|
||||
className="control select is-fullwidth"
|
||||
>
|
||||
<select
|
||||
aria-labelledby="branch-select_0"
|
||||
disabled={false}
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
@@ -3048,7 +3049,6 @@ exports[`Storyshots Forms/Radio Default 1`] = `
|
||||
className="radio mr-2"
|
||||
>
|
||||
<input
|
||||
aria-describedby="radio_56"
|
||||
aria-labelledby="radio_55"
|
||||
checked={false}
|
||||
onBlur={[Function]}
|
||||
@@ -3070,7 +3070,6 @@ exports[`Storyshots Forms/Radio Default 1`] = `
|
||||
className="radio mr-2"
|
||||
>
|
||||
<input
|
||||
aria-describedby="radio_58"
|
||||
aria-labelledby="radio_57"
|
||||
checked={true}
|
||||
onBlur={[Function]}
|
||||
@@ -3100,7 +3099,6 @@ exports[`Storyshots Forms/Radio Disabled 1`] = `
|
||||
disabled={true}
|
||||
>
|
||||
<input
|
||||
aria-describedby="radio_60"
|
||||
aria-labelledby="radio_59"
|
||||
checked={true}
|
||||
disabled={true}
|
||||
@@ -3128,7 +3126,6 @@ Array [
|
||||
className="radio mr-2"
|
||||
>
|
||||
<input
|
||||
aria-describedby="radio_68"
|
||||
aria-labelledby="radio_67"
|
||||
checked={false}
|
||||
onBlur={[Function]}
|
||||
@@ -3160,7 +3157,6 @@ exports[`Storyshots Forms/Radio ReactHookForm 1`] = `
|
||||
className="radio mr-2"
|
||||
>
|
||||
<input
|
||||
aria-describedby="radio_70"
|
||||
aria-labelledby="radio_69"
|
||||
defaultChecked={true}
|
||||
name="rememberMe"
|
||||
@@ -3184,7 +3180,6 @@ exports[`Storyshots Forms/Radio ReactHookForm 1`] = `
|
||||
className="radio mr-2"
|
||||
>
|
||||
<input
|
||||
aria-describedby="radio_72"
|
||||
aria-labelledby="radio_71"
|
||||
name="rememberMe"
|
||||
onBlur={[Function]}
|
||||
@@ -3208,7 +3203,6 @@ exports[`Storyshots Forms/Radio ReactHookForm 1`] = `
|
||||
className="radio mr-2 ml-2"
|
||||
>
|
||||
<input
|
||||
aria-describedby="radio_74"
|
||||
aria-labelledby="radio_73"
|
||||
name="scramblePassword"
|
||||
onBlur={[Function]}
|
||||
@@ -3232,7 +3226,6 @@ exports[`Storyshots Forms/Radio ReactHookForm 1`] = `
|
||||
disabled={true}
|
||||
>
|
||||
<input
|
||||
aria-describedby="radio_76"
|
||||
aria-labelledby="radio_75"
|
||||
disabled={true}
|
||||
name="disabled"
|
||||
@@ -3256,7 +3249,6 @@ exports[`Storyshots Forms/Radio ReactHookForm 1`] = `
|
||||
className="radio mr-2"
|
||||
>
|
||||
<input
|
||||
aria-describedby="radio_78"
|
||||
aria-labelledby="radio_77"
|
||||
name="readonly"
|
||||
onBlur={[Function]}
|
||||
@@ -3298,7 +3290,6 @@ Array [
|
||||
className="radio mr-2"
|
||||
>
|
||||
<input
|
||||
aria-describedby="radio_66"
|
||||
aria-labelledby="radio_65"
|
||||
checked={false}
|
||||
onBlur={[Function]}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
import React, { ChangeEvent, FC, FocusEvent } from "react";
|
||||
import React, { ChangeEvent, FC, FocusEvent, useMemo } from "react";
|
||||
import classNames from "classnames";
|
||||
import { Help } from "../index";
|
||||
import { createFormFieldWrapper, FieldProps, FieldType, isLegacy, isUsingRef } from "./FormFieldTypes";
|
||||
@@ -47,8 +47,8 @@ const InnerRadio: FC<FieldProps<BaseProps, HTMLInputElement, boolean>> = ({
|
||||
ariaLabelledby,
|
||||
...props
|
||||
}) => {
|
||||
const id = ariaLabelledby || createA11yId("radio");
|
||||
const helpId = createA11yId("radio");
|
||||
const id = useMemo(() => ariaLabelledby || createA11yId("radio"), [ariaLabelledby]);
|
||||
const helpId = useMemo(() => createA11yId("radio"), []);
|
||||
|
||||
const renderHelp = () => {
|
||||
const helpText = props.helpText;
|
||||
@@ -77,7 +77,7 @@ const InnerRadio: FC<FieldProps<BaseProps, HTMLInputElement, boolean>> = ({
|
||||
}
|
||||
};
|
||||
|
||||
const labelElement = props.label ? (<span id={id}>{props.label}</span>) : null;
|
||||
const labelElement = props.label ? <span id={id}>{props.label}</span> : null;
|
||||
|
||||
return (
|
||||
<fieldset className="is-inline-block" disabled={readOnly}>
|
||||
@@ -98,7 +98,7 @@ const InnerRadio: FC<FieldProps<BaseProps, HTMLInputElement, boolean>> = ({
|
||||
ref={props.innerRef}
|
||||
defaultChecked={defaultChecked}
|
||||
aria-labelledby={id}
|
||||
aria-describedby={helpId}
|
||||
aria-describedby={props.helpText ? helpId : undefined}
|
||||
/>{" "}
|
||||
{labelElement}
|
||||
{renderHelp()}
|
||||
|
||||
@@ -124,7 +124,7 @@ const InnerSelect: FC<FieldProps<BaseProps, HTMLSelectElement, string>> = ({
|
||||
onChange={handleInput}
|
||||
onBlur={handleBlur}
|
||||
disabled={disabled}
|
||||
aria-labelledby={label ? a11yId : undefined}
|
||||
aria-labelledby={ariaLabelledby || (label ? a11yId : undefined)}
|
||||
aria-describedby={helpText ? helpId : undefined}
|
||||
{...createAttributesForTesting(testId)}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user