Skip to content

Commit

Permalink
use aria-query
Browse files Browse the repository at this point in the history
  • Loading branch information
tanhauhau committed Jul 10, 2022
1 parent e954af2 commit b4dc1a4
Show file tree
Hide file tree
Showing 5 changed files with 13 additions and 136 deletions.
4 changes: 2 additions & 2 deletions site/content/docs/05-accessibility-warnings.md
Expand Up @@ -101,10 +101,10 @@ Enforce img alt attribute does not contain the word image, picture, or photo. Sc
### `a11y-incorrect-aria-attribute-type`

Enforce that only the correct type of value is used for aria attributes. For example, `aria-hidden`
should only receive a boolean or `undefined`.
should only receive a boolean.

```sv
<!-- A11y: The value of 'aria-hidden' must be exactly one of true, false, or undefined -->
<!-- A11y: The value of 'aria-hidden' must be exactly one of true or false -->
<div aria-hidden="yes"/>
```

Expand Down
11 changes: 5 additions & 6 deletions src/compiler/compile/compiler_warnings.ts
@@ -1,5 +1,7 @@
// All compiler warnings should be listed and accessed from here

import { ARIAPropertyDefinition } from 'aria-query';

/**
* @internal
*/
Expand Down Expand Up @@ -60,15 +62,12 @@ export default {
code: 'a11y-aria-attributes',
message: `A11y: <${name}> should not have aria-* attributes`
}),
a11y_incorrect_attribute_type: (schema: any, attribute: string) => {
a11y_incorrect_attribute_type: (schema: ARIAPropertyDefinition, attribute: string) => {
let message;
switch (schema.type) {
case 'boolean':
message = `The value of '${attribute}' must be exactly one of true or false`;
break;
case 'boolean_or_undefined':
message = `The value of '${attribute}' must be exactly one of true, false, or undefined`;
break;
case 'id':
message = `The value of '${attribute}' must be a string that represents a DOM element ID`;
break;
Expand All @@ -79,10 +78,10 @@ export default {
message = `The value of '${attribute}' must be exactly one of true, false, or mixed`;
break;
case 'token':
message = `The value of '${attribute}' must be exactly one of ${schema.whitelist.join(', ')}`;
message = `The value of '${attribute}' must be exactly one of ${(schema.values || []).join(', ')}`;
break;
case 'tokenlist':
message = `The value of '${attribute}' must be a space-separated list of one or more of ${schema.whitelist.join(', ')}`;
message = `The value of '${attribute}' must be a space-separated list of one or more of ${(schema.values || []).join(', ')}`;
break;
default:
message = `The value of '${attribute}' must be of type ${schema.type}`;
Expand Down
94 changes: 6 additions & 88 deletions src/compiler/compile/nodes/Element.ts
Expand Up @@ -23,7 +23,7 @@ import { string_literal } from '../utils/stringify';
import { Literal } from 'estree';
import compiler_warnings from '../compiler_warnings';
import compiler_errors from '../compiler_errors';
import { ARIARoleDefintionKey, roles } from 'aria-query';
import { ARIARoleDefintionKey, roles, aria, ARIAPropertyDefinition, ARIAProperty } from 'aria-query';

const svg = /^(?:altGlyph|altGlyphDef|altGlyphItem|animate|animateColor|animateMotion|animateTransform|circle|clipPath|color-profile|cursor|defs|desc|discard|ellipse|feBlend|feColorMatrix|feComponentTransfer|feComposite|feConvolveMatrix|feDiffuseLighting|feDisplacementMap|feDistantLight|feDropShadow|feFlood|feFuncA|feFuncB|feFuncG|feFuncR|feGaussianBlur|feImage|feMerge|feMergeNode|feMorphology|feOffset|fePointLight|feSpecularLighting|feSpotLight|feTile|feTurbulence|filter|font|font-face|font-face-format|font-face-name|font-face-src|font-face-uri|foreignObject|g|glyph|glyphRef|hatch|hatchpath|hkern|image|line|linearGradient|marker|mask|mesh|meshgradient|meshpatch|meshrow|metadata|missing-glyph|mpath|path|pattern|polygon|polyline|radialGradient|rect|set|solidcolor|stop|svg|switch|symbol|text|textPath|tref|tspan|unknown|use|view|vkern)$/;

Expand All @@ -46,85 +46,6 @@ const a11y_required_attributes = {
object: ['title', 'aria-label', 'aria-labelledby']
};

// Full list of attributes from https://www.w3.org/TR/wai-aria
const a11y_attribute_types = new Map([
['activedescendant', { type: 'id'}],
['atomic', { type: 'boolean' }],
['autocomplete', {
type: 'token',
whitelist: 'inline list both none'.split(' ')
}],
['busy', { type: 'boolean'}],
['checked', { type: 'tristate' }],
['colcount', { type: 'integer'}],
['colindex', { type: 'integer'}],
['colspan', { type: 'integer'}],
['controls', { type: 'idlist'}],
['current', {
type: 'token',
whitelist: 'page step location date time true false'.split(' ')
}],
['details', { type: 'id'}],
['describedby', { type: 'idlist'}],
['disabled', { type: 'boolean'}],
['dropeffect', {
type: 'tokenlist',
whitelist: 'copy execute link move none popup'.split(' ')
}],
['errormessage', { type: 'id'}],
['expanded', { type: 'boolean_or_undefined' }],
['flowto', { type: 'idlist'}],
['grabbed', { type: 'boolean_or_undefined'}],
['haspopup', {
type: 'token',
whitelist: 'false true menu listbox tree grid dialog'.split(' ')
}],
['hidden', { type: 'boolean_or_undefined' }],
['invalid', {
type: 'token',
whitelist: 'grammar false spelling true'.split(' ')
}],
['keyshortcuts', { type: 'string' }],
['label', { type: 'string' }],
['labelledby', { type: 'idlist' }],
['level', { type: 'integer' }],
['live', {
type: 'token',
whitelist: 'assertive off polite'.split(' ')
}],
['modal', { type: 'boolean' }],
['multiline', { type: 'boolean' }],
['multiselectable', { type: 'boolean' }],
['orientation', {
type: 'token',
whitelist: 'vertical undefined horizontal'.split(' ')
}],
['owns', { type: 'idlist' }],
['placeholder', { type: 'string' }],
['posinset', { type: 'integer' }],
['pressed', { type: 'tristate' }],
['readonly', { type: 'boolean' }],
['relevant', {
type: 'tokenlist',
whitelist: 'additions all removals text'.split(' ')
}],
['required', { type: 'boolean' }],
['roledescription', { type: 'string' }],
['rowcount', { type: 'integer' }],
['rowindex', { type: 'integer' }],
['rowspan', { type: 'integer' }],
['selected', { type: 'boolean_or_undefined' }],
['setsize', { type: 'integer' }],
['sort', {
type: 'token',
whitelist: 'ascending descending none other'.split(' ')
}],
['valuemax', { type: 'number' }],
['valuemin', { type: 'number' }],
['valuenow', { type: 'number' }],
['valuetext', { type: 'string' }]
]);

const a11y_distracting_elements = new Set([
'blink',
'marquee'
Expand Down Expand Up @@ -256,30 +177,27 @@ function get_namespace(parent: Element, element: Element, explicit_namespace: st
return parent_element.namespace;
}

function is_valid_aria_attribute_value(schema: any, value: any): boolean {
function is_valid_aria_attribute_value(schema: ARIAPropertyDefinition, value: string | boolean): boolean {
switch (schema.type) {
case 'boolean':
case 'boolean_or_undefined':
return typeof value === 'boolean';
case 'string':
return typeof value === 'string';
case 'id':
return typeof value === 'string';
case 'tristate':
return typeof value === 'boolean' || value === 'mixed';
case 'integer':
return typeof value !== 'boolean' && isNaN(Number(value)) === false;
case 'number':
return typeof value !== 'boolean' && isNaN(Number(value)) === false;
case 'token': // single token
return schema.whitelist
return (schema.values || [])
.indexOf(typeof value === 'string' ? value.toLowerCase() : value) > -1;
case 'idlist': // if list of ids, split each
return typeof value === 'string'
&& value.split(' ').every((id) => typeof id === 'string');
case 'tokenlist': // if list of tokens, split each
return typeof value === 'string'
&& value.split(' ').every((token) => schema.whitelist.indexOf(token.toLowerCase()) > -1);
&& value.split(' ').every((token) => (schema.values || []).indexOf(token.toLowerCase()) > -1);
default:
return false;
}
Expand Down Expand Up @@ -545,8 +463,8 @@ export default class Element extends Node {
if (value === 'true') value = true;
if (value === 'false') value = false;

if (value !== null && value !== undefined && a11y_attribute_types.has(type)) {
const schema = a11y_attribute_types.get(type);
if (value !== null && value !== undefined && aria.has(name as ARIAProperty)) {
const schema = aria.get(name as ARIAProperty);
if (!is_valid_aria_attribute_value(schema, value)) {
component.warn(attribute, compiler_warnings.a11y_incorrect_attribute_type(schema, name));
}
Expand Down

This file was deleted.

This file was deleted.

0 comments on commit b4dc1a4

Please sign in to comment.