fonteva-design-guide
Version:
## Dev, Build and Test
129 lines (113 loc) • 4.94 kB
JavaScript
const validateWcag = (element, tagName) => {
let isValid = true;
wcagValidations[tagName].forEach(validation => {
if (validation.validate(element) === false) {
isValid = false;
['fix', 'log'].forEach(action => {
if (typeof validation[action] === 'function') {
validation[action](element);
}
});
}
});
return isValid;
};
const hasChildImageAltText = element => {
return (
[...element.querySelectorAll('img')].reduce((altText, imgElement) => {
if (imgElement.hasAttribute('alt')) {
altText += imgElement.getAttribute('alt');
}
return altText;
}, '') !== ''
);
};
const regexMatchesList = (matchList, testMatch) => {
const regex = new RegExp(`[^\\W](${matchList.join('|')})[$\\W]`, 'i');
return regex.test(testMatch);
};
const isValidId = (element, id) => element.parentNode.querySelector(`[id^="${id}"]`) !== null;
const checkProperty = (component, property) => component[property] !== null && component[property] !== '';
const checkAttribute = (element, attribute) =>
element.hasAttribute(attribute) && element.hasAttribute(attribute) !== '';
const checkAttributeMatch = (element, attribute, matches) =>
checkAttribute(element, attribute) && regexMatchesList(matches, element.getAttribute(attribute));
const hasAriaPresentationRole = element => checkAttributeMatch(element, 'aria-role', ['presentation', 'none']);
const hasValidAriaLabelledBy = element =>
checkAttribute(element, 'aria-labelledby') && isValidId(element, element.getAttribute('aria-labelledby'));
const hasPresentationAlt = element => checkAttributeMatch(element, 'alt', ['spacer', 'placeholder']);
const hasExtraneousAlt = element => checkAttributeMatch(element, 'alt', ['image', 'photo']);
const validateLinkOrButton = element => {
return (
element.innerText !== '' || // - Visible text inside the tag
(checkAttribute(element, 'title') && (element.innerText !== '' || checkAttribute(element, 'aria-label'))) || // - A title attribute
checkAttribute(element, 'aria-label') || // - An aria-label attribute
hasValidAriaLabelledBy(element) || // - An aria-labelledby attribute with one of more IDs that reference valid elements
hasAriaPresentationRole(element) || // - An aria-role of "presentation" or "none"
hasChildImageAltText(element) // - An image with an alt attribute
);
};
const validateInputField = component => {
return component.hasLabel || checkProperty(component, 'ariaLabel');
};
const wcagValidations = {
/*
<img> - All image tags must have at least one of the following:
*/
img: [
{
validate: element => element.hasAttribute('alt'), // An alt attribute, empty if the image is for presentation not content
fix: element => element.setAttribute('alt', ''),
log: element =>
console.debug(
`ALT attribute missing from tag:\n\t${element.outerHTML}\n\t(Setting ALT to empty string.)`
)
},
{
validate: element => !hasPresentationAlt(element), // A non-placeholder/spaces/etc alt attribute
fix: element => element.setAttribute('alt', ''),
log: element =>
console.debug(`ALT attribute contains presentation terms:\n\t${element.outerHTML}
(Setting ALT to empty string.)`)
},
{
validate: element => !hasExtraneousAlt(element), // An alt attribute that describes the image without using the word image or photo or similar
log: element =>
console.debug(`ALT attribute contains extraneous description terms:\n\t${element.outerHTML}`)
}
],
/*
<a> - All link tags must have at least one of the following:
*/
a: [
{
validate: validateLinkOrButton,
log: element =>
console.debug(`Link tag does not have accessible text:\n\t${element.outerHTML}
(No fix applied)`)
}
],
/*
<button> - All button tags must have at least one of the following:
*/
button: [
{
validate: validateLinkOrButton,
log: element =>
console.debug(`Button tag does not have accessible text:\n\t${element.outerHTML}
(No fix applied)`)
}
],
/*
<input> - All input fields must have a label
*/
input: [
{
validate: validateInputField,
log: element =>
console.debug(`Input field does not have an accessible label:\n\t${element.outerHTML}
(No fix applied)`)
}
]
};
export { validateWcag as default };