mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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:
parent
6856784134
commit
fca252e7dc
@ -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",
|
||||
|
@ -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>}
|
||||
|
@ -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"
|
||||
/>
|
||||
|
@ -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>
|
||||
);
|
||||
}}
|
||||
|
@ -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{
|
||||
|
@ -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>
|
||||
)}
|
||||
|
@ -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" />;
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
);
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
);
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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 ? (
|
||||
|
@ -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)}
|
||||
|
@ -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>
|
||||
);
|
||||
|
@ -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>
|
||||
);
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user