A11y: enable rule jsx-a11y/alt-text (#55832)

* Enable jsx-a11y/alt-text rule

* Fix errors

* Fix tests

* Enable jsx-a11y/alt-text rule after solving merge conflict

* Delete unused import

* Modify files according to the reviewer's comments

* Revert test changes and update snapshot

* tweaks to image alt names

Co-authored-by: Ashley Harrison <ashley.harrison@grafana.com>
This commit is contained in:
Laura Fernández 2022-10-03 10:27:04 +02:00 committed by GitHub
parent 6856784134
commit fca252e7dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 32 additions and 21 deletions

View File

@ -67,7 +67,7 @@
// we should fix them one by one and mark them as errors
// once they're all fixed, we can remove them all and instead extend the strict preset
// with "extends": ["plugin:jsx-a11y/strict"]
"jsx-a11y/alt-text": "off",
"jsx-a11y/alt-text": "error",
"jsx-a11y/anchor-has-content": "error",
"jsx-a11y/anchor-is-valid": "off",
"jsx-a11y/aria-activedescendant-has-tabindex": "error",

View File

@ -17,7 +17,7 @@ export const SelectOption = (props: ExtendedOptionProps) => {
return (
<components.Option {...props}>
<div className="gf-form-select-box__desc-option">
{data.imgUrl && <img className="gf-form-select-box__desc-option__img" src={data.imgUrl} />}
{data.imgUrl && <img className="gf-form-select-box__desc-option__img" src={data.imgUrl} alt="" />}
<div className="gf-form-select-box__desc-option__body">
<div>{children}</div>
{data.description && <div className="gf-form-select-box__desc-option__desc">{data.description}</div>}

View File

@ -14,6 +14,7 @@ exports[`SelectOption renders correctly 1`] = `
className="gf-form-select-box__desc-option"
>
<img
alt=""
className="gf-form-select-box__desc-option__img"
src="url/to/avatar"
/>

View File

@ -15,13 +15,13 @@ export const ImageCell: FC<TableCellProps> = (props) => {
return (
<div {...cellProps} className={tableStyles.cellContainer}>
{!hasLinks && <img src={displayValue.text} className={tableStyles.imageCell} />}
{!hasLinks && <img src={displayValue.text} className={tableStyles.imageCell} alt="" />}
{hasLinks && (
<DataLinksContextMenu links={() => getCellLinks(field, row) || []}>
{(api) => {
return (
<div onClick={api.openMenu} className={cx(tableStyles.imageCellLink, api.targetClassName)}>
<img src={displayValue.text} className={tableStyles.imageCell} />
<img src={displayValue.text} className={tableStyles.imageCell} alt="" />
</div>
);
}}

View File

@ -291,7 +291,7 @@ func signRPMRepo(repoRoot string, cfg PublishConfig) error {
PrimaryKey: pubKey,
PrivateKey: privKey,
Identities: map[string]*openpgp.Identity{
uid.Id: &openpgp.Identity{
uid.Id: {
Name: uid.Name,
UserId: uid,
SelfSignature: &packet.Signature{

View File

@ -48,7 +48,7 @@ export function TopSearchBar() {
{profileNode && (
<Dropdown overlay={<TopNavBarMenu node={profileNode} />}>
<button className={styles.actionItem}>
<img src={contextSrv.user.gravatarUrl} />
<img src={contextSrv.user.gravatarUrl} alt="User avatar" />
</button>
</Dropdown>
)}

View File

@ -11,10 +11,10 @@ const setClassNameHelper = (inherited: boolean) => {
function ItemAvatar({ item }: { item: DashboardAcl }) {
if (item.userAvatarUrl) {
return <img className="filter-table__avatar" src={item.userAvatarUrl} />;
return <img className="filter-table__avatar" src={item.userAvatarUrl} alt="User avatar" />;
}
if (item.teamAvatarUrl) {
return <img className="filter-table__avatar" src={item.teamAvatarUrl} />;
return <img className="filter-table__avatar" src={item.teamAvatarUrl} alt="Team avatar" />;
}
if (item.role === 'Editor') {
return <Icon size="lg" name="edit" />;

View File

@ -32,7 +32,7 @@ const RuleType: FC<Props> = (props) => {
return (
<Card className={cardStyles} isSelected={selected} onClick={() => onClick(value)} disabled={disabled}>
<Card.Figure>
<img src={image} />
<img src={image} alt="" />
</Card.Figure>
<Card.Heading>{name}</Card.Heading>
<Card.Description>{description}</Card.Description>

View File

@ -184,7 +184,7 @@ const DataSourceCell = memo(
return (
<span className={styles.root}>
<img src={value.meta.info.logos.small} className={styles.dsLogo} />
<img src={value.meta.info.logos.small} alt="" className={styles.dsLogo} />
{value.name}
</span>
);

View File

@ -70,10 +70,10 @@ export class PreviewSettings extends PureComponent<Props, State> {
</tr>
<tr>
<td>
<img src={getThumbnailURL(uid, false)} style={imgstyle} />
<img src={getThumbnailURL(uid, false)} alt="Preview of dashboard in dark theme" style={imgstyle} />
</td>
<td>
<img src={getThumbnailURL(uid, true)} style={imgstyle} />
<img src={getThumbnailURL(uid, true)} alt="Preview of dashboard in light theme" style={imgstyle} />
</td>
</tr>
<tr>

View File

@ -11,7 +11,7 @@ export const PubdashFooter = function () {
<div className={styles.footer}>
<span className={styles.logoText}>
<a href="https://grafana.com/" target="_blank" rel="noreferrer noopener">
powered by Grafana <img className={styles.logoImg} src="public/img/grafana_icon.svg"></img>
powered by Grafana <img className={styles.logoImg} alt="" src="public/img/grafana_icon.svg"></img>
</a>
</span>
</div>

View File

@ -37,7 +37,7 @@ export const FileUploader = ({ mediaType, setFormData, setUpload, error }: Props
<Field label="Preview">
<div className={styles.iconPreview}>
{mediaType === MediaType.Icon && <SVG src={file} className={styles.img} />}
{mediaType === MediaType.Image && <img src={file} className={styles.img} />}
{mediaType === MediaType.Image && <img src={file} alt="Preview of the uploaded file" className={styles.img} />}
</div>
</Field>
);

View File

@ -40,7 +40,7 @@ function Cell(props: CellProps) {
{card.imgUrl.endsWith('.svg') ? (
<SVG src={card.imgUrl} className={styles.img} />
) : (
<img src={card.imgUrl} className={styles.img} />
<img src={card.imgUrl} alt="" className={styles.img} />
)}
<h6 className={styles.text}>{card.label.slice(0, -4)}</h6>
</div>

View File

@ -34,7 +34,9 @@ export const URLPickerTab = (props: Props) => {
<Field label="Preview">
<div className={styles.iconPreview}>
{mediaType === MediaType.Icon && <SVG src={imgSrc} className={styles.img} />}
{mediaType === MediaType.Image && newValue && <img src={imgSrc} className={styles.img} />}
{mediaType === MediaType.Image && newValue && (
<img src={imgSrc} alt="Preview of the selected URL" className={styles.img} />
)}
</div>
</Field>
<Label>{shortName}</Label>

View File

@ -130,7 +130,13 @@ export function SearchCard({ editable, item, onTagSelected, onToggleChecked, onC
onClick={onCheckboxClick}
/>
{hasImage ? (
<img loading="lazy" className={styles.image} src={imageSrc} onError={() => setHasImage(false)} />
<img
loading="lazy"
className={styles.image}
src={imageSrc}
alt="Dashboard preview"
onError={() => setHasImage(false)}
/>
) : (
<div className={styles.imagePlaceholder}>
{item.icon ? (

View File

@ -33,6 +33,7 @@ export function SearchCardExpanded({ className, imageHeight, imageWidth, item, l
{hasImage ? (
<img
loading="lazy"
alt="Dashboard preview"
className={styles.image}
src={imageSrc}
onLoad={() => setHasImage(true)}

View File

@ -302,7 +302,7 @@ function makeDataSourceColumn(
onDatasourceChange(settings.uid);
}}
>
<img src={icon} width={14} height={14} title={settings.type} className={iconClass} />
<img src={icon} alt="" width={14} height={14} title={settings.type} className={iconClass} />
{settings.name}
</span>
);

View File

@ -62,7 +62,7 @@ export function FileView({ listing, path, onPathChange, view }: Props) {
return (
<div>
<a target={'_self'} href={src}>
<img src={src} className={styles.img} />
<img src={src} alt="File preview" className={styles.img} />
</a>
</div>
);

View File

@ -168,7 +168,7 @@ export function DashboardQueryEditor({ panelData, queries, onChange, onRunQuerie
{results.map((target, i) => (
<div className={styles.queryEditorRowHeader} key={`DashboardQueryRow-${i}`}>
<div>
<img src={target.img} width={16} />
<img src={target.img} alt="" width={16} />
<span className={styles.refId}>{target.refId}:</span>
</div>
<div>

View File

@ -923,6 +923,7 @@ export class PrometheusDatasource
<img
style={{ width: 14, height: 14, verticalAlign: 'text-bottom' }}
src={LOGOS[buildInfo.application ?? PromApplication.Prometheus]}
alt=""
/>{' '}
{buildInfo.application ? AppDisplayNames[buildInfo.application] : 'Unknown'}
</span>

View File

@ -37,7 +37,7 @@ export const AnnotationTooltip = ({
const ts = <span className={styles.time}>{Boolean(annotation.isRegion) ? `${time} - ${timeEnd}` : time}</span>;
if (annotation.login && annotation.avatarUrl) {
avatar = <img className={styles.avatar} src={annotation.avatarUrl} />;
avatar = <img className={styles.avatar} alt="Annotation avatar" src={annotation.avatarUrl} />;
}
if (annotation.alertId !== undefined && annotation.newState) {