/**
* @fileoverview Enforce all elements that require alternative text have it.
* @author Ethan Cohen
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
import { getProp, getPropValue, elementType, getLiteralPropValue } from 'jsx-ast-utils';
import { generateObjSchema, arraySchema } from '../util/schemas';
import hasAccessibleChild from '../util/hasAccessibleChild';
import isPresentationRole from '../util/isPresentationRole';
const DEFAULT_ELEMENTS = [
'img',
'object',
'area',
'input[type="image"]',
];
const schema = generateObjSchema({
elements: arraySchema,
img: arraySchema,
object: arraySchema,
area: arraySchema,
'input[type="image"]': arraySchema,
});
const ruleByElement = {
img(context, node) {
const nodeType = elementType(node);
const altProp = getProp(node.attributes, 'alt');
// Missing alt prop error.
if (altProp === undefined) {
if (isPresentationRole(nodeType, node.attributes)) {
context.report({
node,
message: 'Prefer alt="" over a presentational role. First rule of aria is to not use aria if it can be achieved via native HTML.',
});
return;
}
context.report({
node,
message: `${nodeType} elements must have an alt prop, either with meaningful text, or an empty string for decorative images.`,
});
return;
}
// Check if alt prop is undefined.
const altValue = getPropValue(altProp);
const isNullValued = altProp.value === null; //
if ((altValue && !isNullValued) || altValue === '') {
return;
}
// Undefined alt prop error.
context.report({
node,
message: `Invalid alt value for ${nodeType}. Use alt="" for presentational images.`,
});
},
object(context, node) {
const ariaLabelProp = getProp(node.attributes, 'aria-label');
const arialLabelledByProp = getProp(node.attributes, 'aria-labelledby');
const hasLabel = ariaLabelProp !== undefined || arialLabelledByProp !== undefined;
const titleProp = getLiteralPropValue(getProp(node.attributes, 'title'));
const hasTitleAttr = !!titleProp;
if (hasLabel || hasTitleAttr || hasAccessibleChild(node.parent)) {
return;
}
context.report({
node,
message: 'Embedded