Lint and format src/compiler/compile/utils/a11y.js

pull/8569/head
Simon Holthausen 3 years ago
parent 8d8739cbc4
commit 186b8ebf20

@ -4,52 +4,61 @@ import { regex_whitespaces } from '../../utils/patterns.js';
const aria_roles = roles_map.keys(); const aria_roles = roles_map.keys();
const abstract_roles = new Set(aria_roles.filter((role) => roles_map.get(role).abstract)); const abstract_roles = new Set(aria_roles.filter((role) => roles_map.get(role).abstract));
const non_abstract_roles = aria_roles.filter((name) => !abstract_roles.has(name)); const non_abstract_roles = aria_roles.filter((name) => !abstract_roles.has(name));
const non_interactive_roles = new Set(non_abstract_roles const non_interactive_roles = new Set(
.filter((name) => { non_abstract_roles
const role = roles_map.get(name); .filter((name) => {
return ( const role = roles_map.get(name);
// 'toolbar' does not descend from widget, but it does support return (
// aria-activedescendant, thus in practice we treat it as a widget. // 'toolbar' does not descend from widget, but it does support
// focusable tabpanel elements are recommended if any panels in a set contain content where the first element in the panel is not focusable. // aria-activedescendant, thus in practice we treat it as a widget.
// 'generic' is meant to have no semantic meaning. // focusable tabpanel elements are recommended if any panels in a set contain content where the first element in the panel is not focusable.
!['toolbar', 'tabpanel', 'generic'].includes(name) && // 'generic' is meant to have no semantic meaning.
!role.superClass.some((classes) => classes.includes('widget'))); !['toolbar', 'tabpanel', 'generic'].includes(name) &&
}) !role.superClass.some((classes) => classes.includes('widget'))
.concat( );
// The `progressbar` is descended from `widget`, but in practice, its })
// value is always `readonly`, so we treat it as a non-interactive role. .concat(
'progressbar')); // The `progressbar` is descended from `widget`, but in practice, its
const interactive_roles = new Set(non_abstract_roles.filter((name) => !non_interactive_roles.has(name) && // value is always `readonly`, so we treat it as a non-interactive role.
// 'generic' is meant to have no semantic meaning. 'progressbar'
name !== 'generic')); )
);
const interactive_roles = new Set(
non_abstract_roles.filter(
(name) =>
!non_interactive_roles.has(name) &&
// 'generic' is meant to have no semantic meaning.
name !== 'generic'
)
);
/** /**
* @param {ARIARoleDefinitionKey} role * @param {ARIARoleDefinitionKey} role
*/ */
export function is_non_interactive_roles(role) { export function is_non_interactive_roles(role) {
return non_interactive_roles.has(role); return non_interactive_roles.has(role);
} }
/** /**
* @param {ARIARoleDefinitionKey} role * @param {ARIARoleDefinitionKey} role
*/ */
export function is_interactive_roles(role) { export function is_interactive_roles(role) {
return interactive_roles.has(role); return interactive_roles.has(role);
} }
/** /**
* @param {ARIARoleDefinitionKey} role * @param {ARIARoleDefinitionKey} role
*/ */
export function is_abstract_role(role) { export function is_abstract_role(role) {
return abstract_roles.has(role); return abstract_roles.has(role);
} }
const presentation_roles = new Set(['presentation', 'none']); const presentation_roles = new Set(['presentation', 'none']);
/** /**
* @param {ARIARoleDefinitionKey} role * @param {ARIARoleDefinitionKey} role
*/ */
export function is_presentation_role(role) { export function is_presentation_role(role) {
return presentation_roles.has(role); return presentation_roles.has(role);
} }
/** /**
@ -57,80 +66,84 @@ export function is_presentation_role(role) {
* @param {Map<string, Attribute>} attribute_map * @param {Map<string, Attribute>} attribute_map
*/ */
export function is_hidden_from_screen_reader(tag_name, attribute_map) { export function is_hidden_from_screen_reader(tag_name, attribute_map) {
if (tag_name === 'input') { if (tag_name === 'input') {
const type = attribute_map.get('type')?.get_static_value(); const type = attribute_map.get('type')?.get_static_value();
if (type && type === 'hidden') { if (type && type === 'hidden') {
return true; return true;
} }
} }
const aria_hidden = attribute_map.get('aria-hidden'); const aria_hidden = attribute_map.get('aria-hidden');
if (!aria_hidden) if (!aria_hidden) return false;
return false; if (!aria_hidden.is_static) return true;
if (!aria_hidden.is_static) const aria_hidden_value = aria_hidden.get_static_value();
return true; return aria_hidden_value === true || aria_hidden_value === 'true';
const aria_hidden_value = aria_hidden.get_static_value();
return aria_hidden_value === true || aria_hidden_value === 'true';
} }
/** /**
* @param {Map<string, Attribute>} attribute_map * @param {Map<string, Attribute>} attribute_map
*/ */
export function has_disabled_attribute(attribute_map) { export function has_disabled_attribute(attribute_map) {
const disabled_attr = attribute_map.get('disabled'); const disabled_attr = attribute_map.get('disabled');
const disabled_attr_value = disabled_attr && disabled_attr.get_static_value(); const disabled_attr_value = disabled_attr && disabled_attr.get_static_value();
if (disabled_attr_value) { if (disabled_attr_value) {
return true; return true;
} }
const aria_disabled_attr = attribute_map.get('aria-disabled'); const aria_disabled_attr = attribute_map.get('aria-disabled');
if (aria_disabled_attr) { if (aria_disabled_attr) {
const aria_disabled_attr_value = aria_disabled_attr.get_static_value(); const aria_disabled_attr_value = aria_disabled_attr.get_static_value();
if (aria_disabled_attr_value === true) { if (aria_disabled_attr_value === true) {
return true; return true;
} }
} }
return false; return false;
} }
/** /**
* @type {ARIARoleRelationConcept[]} * @type {ARIARoleRelationConcept[]}
*/ */
const non_interactive_element_role_schemas = []; const non_interactive_element_role_schemas = [];
elementRoles.entries().forEach(([schema, roles]) => { elementRoles.entries().forEach(([schema, roles]) => {
if ([...roles].every((role) => role !== 'generic' && non_interactive_roles.has(role))) { if ([...roles].every((role) => role !== 'generic' && non_interactive_roles.has(role))) {
non_interactive_element_role_schemas.push(schema); non_interactive_element_role_schemas.push(schema);
} }
}); });
/** /**
* @type {ARIARoleRelationConcept[]} * @type {ARIARoleRelationConcept[]}
*/ */
const interactive_element_role_schemas = []; const interactive_element_role_schemas = [];
elementRoles.entries().forEach(([schema, roles]) => { elementRoles.entries().forEach(([schema, roles]) => {
if ([...roles].every((role) => interactive_roles.has(role))) { if ([...roles].every((role) => interactive_roles.has(role))) {
interactive_element_role_schemas.push(schema); interactive_element_role_schemas.push(schema);
} }
}); });
const interactive_ax_objects = new Set([...AXObjects.keys()].filter((name) => AXObjects.get(name).type === 'widget')); const interactive_ax_objects = new Set(
const non_interactive_ax_objects = new Set([...AXObjects.keys()].filter((name) => ['windows', 'structure'].includes(AXObjects.get(name).type))); [...AXObjects.keys()].filter((name) => AXObjects.get(name).type === 'widget')
);
const non_interactive_ax_objects = new Set(
[...AXObjects.keys()].filter((name) =>
['windows', 'structure'].includes(AXObjects.get(name).type)
)
);
/** /**
* @type {ARIARoleRelationConcept[]} * @type {ARIARoleRelationConcept[]}
*/ */
const interactive_element_ax_object_schemas = []; const interactive_element_ax_object_schemas = [];
elementAXObjects.entries().forEach(([schema, ax_object]) => { elementAXObjects.entries().forEach(([schema, ax_object]) => {
if ([...ax_object].every((role) => interactive_ax_objects.has(role))) { if ([...ax_object].every((role) => interactive_ax_objects.has(role))) {
interactive_element_ax_object_schemas.push(schema); interactive_element_ax_object_schemas.push(schema);
} }
}); });
/** /**
* @type {ARIARoleRelationConcept[]} * @type {ARIARoleRelationConcept[]}
*/ */
const non_interactive_element_ax_object_schemas = []; const non_interactive_element_ax_object_schemas = [];
elementAXObjects.entries().forEach(([schema, ax_object]) => { elementAXObjects.entries().forEach(([schema, ax_object]) => {
if ([...ax_object].every((role) => non_interactive_ax_objects.has(role))) { if ([...ax_object].every((role) => non_interactive_ax_objects.has(role))) {
non_interactive_element_ax_object_schemas.push(schema); non_interactive_element_ax_object_schemas.push(schema);
} }
}); });
/** /**
@ -139,25 +152,22 @@ elementAXObjects.entries().forEach(([schema, ax_object]) => {
* @param {Map<string, Attribute>} attribute_map * @param {Map<string, Attribute>} attribute_map
*/ */
function match_schema(schema, tag_name, attribute_map) { function match_schema(schema, tag_name, attribute_map) {
if (schema.name !== tag_name) if (schema.name !== tag_name) return false;
return false; if (!schema.attributes) return true;
if (!schema.attributes) return schema.attributes.every((schema_attribute) => {
return true; const attribute = attribute_map.get(schema_attribute.name);
return schema.attributes.every((schema_attribute) => { if (!attribute) return false;
const attribute = attribute_map.get(schema_attribute.name); if (schema_attribute.value && schema_attribute.value !== attribute.get_static_value()) {
if (!attribute) return false;
return false; }
if (schema_attribute.value && schema_attribute.value !== attribute.get_static_value()) { return true;
return false; });
}
return true;
});
} }
export var ElementInteractivity; export var ElementInteractivity;
(function (ElementInteractivity) { (function (ElementInteractivity) {
ElementInteractivity["Interactive"] = "interactive"; ElementInteractivity['Interactive'] = 'interactive';
ElementInteractivity["NonInteractive"] = "non-interactive"; ElementInteractivity['NonInteractive'] = 'non-interactive';
ElementInteractivity["Static"] = "static"; ElementInteractivity['Static'] = 'static';
})(ElementInteractivity || (ElementInteractivity = {})); })(ElementInteractivity || (ElementInteractivity = {}));
/** /**
@ -166,20 +176,34 @@ export var ElementInteractivity;
* @returns {import("C:/repos/svelte/svelte/a11y.ts-to-jsdoc").ElementInteractivity} * @returns {import("C:/repos/svelte/svelte/a11y.ts-to-jsdoc").ElementInteractivity}
*/ */
export function element_interactivity(tag_name, attribute_map) { export function element_interactivity(tag_name, attribute_map) {
if (interactive_element_role_schemas.some((schema) => match_schema(schema, tag_name, attribute_map))) { if (
return ElementInteractivity.Interactive; interactive_element_role_schemas.some((schema) => match_schema(schema, tag_name, attribute_map))
} ) {
if (tag_name !== 'header' && return ElementInteractivity.Interactive;
non_interactive_element_role_schemas.some((schema) => match_schema(schema, tag_name, attribute_map))) { }
return ElementInteractivity.NonInteractive; if (
} tag_name !== 'header' &&
if (interactive_element_ax_object_schemas.some((schema) => match_schema(schema, tag_name, attribute_map))) { non_interactive_element_role_schemas.some((schema) =>
return ElementInteractivity.Interactive; match_schema(schema, tag_name, attribute_map)
} )
if (non_interactive_element_ax_object_schemas.some((schema) => match_schema(schema, tag_name, attribute_map))) { ) {
return ElementInteractivity.NonInteractive; return ElementInteractivity.NonInteractive;
} }
return ElementInteractivity.Static; if (
interactive_element_ax_object_schemas.some((schema) =>
match_schema(schema, tag_name, attribute_map)
)
) {
return ElementInteractivity.Interactive;
}
if (
non_interactive_element_ax_object_schemas.some((schema) =>
match_schema(schema, tag_name, attribute_map)
)
) {
return ElementInteractivity.NonInteractive;
}
return ElementInteractivity.Static;
} }
/** /**
@ -188,7 +212,7 @@ export function element_interactivity(tag_name, attribute_map) {
* @returns {boolean} * @returns {boolean}
*/ */
export function is_interactive_element(tag_name, attribute_map) { export function is_interactive_element(tag_name, attribute_map) {
return element_interactivity(tag_name, attribute_map) === ElementInteractivity.Interactive; return element_interactivity(tag_name, attribute_map) === ElementInteractivity.Interactive;
} }
/** /**
@ -197,7 +221,7 @@ export function is_interactive_element(tag_name, attribute_map) {
* @returns {boolean} * @returns {boolean}
*/ */
export function is_non_interactive_element(tag_name, attribute_map) { export function is_non_interactive_element(tag_name, attribute_map) {
return element_interactivity(tag_name, attribute_map) === ElementInteractivity.NonInteractive; return element_interactivity(tag_name, attribute_map) === ElementInteractivity.NonInteractive;
} }
/** /**
@ -206,7 +230,7 @@ export function is_non_interactive_element(tag_name, attribute_map) {
* @returns {boolean} * @returns {boolean}
*/ */
export function is_static_element(tag_name, attribute_map) { export function is_static_element(tag_name, attribute_map) {
return element_interactivity(tag_name, attribute_map) === ElementInteractivity.Static; return element_interactivity(tag_name, attribute_map) === ElementInteractivity.Static;
} }
/** /**
@ -215,127 +239,125 @@ export function is_static_element(tag_name, attribute_map) {
* @param {Map<string, Attribute>} attribute_map * @param {Map<string, Attribute>} attribute_map
*/ */
export function is_semantic_role_element(role, tag_name, attribute_map) { export function is_semantic_role_element(role, tag_name, attribute_map) {
for (const [schema, ax_object] of elementAXObjects.entries()) { for (const [schema, ax_object] of elementAXObjects.entries()) {
if (schema.name === tag_name && if (
(!schema.attributes || schema.name === tag_name &&
schema.attributes.every((attr) => attribute_map.has(attr.name) && (!schema.attributes ||
attribute_map.get(attr.name).get_static_value() === attr.value))) { schema.attributes.every(
for (const name of ax_object) { (attr) =>
const roles = AXObjectRoles.get(name); attribute_map.has(attr.name) &&
if (roles) { attribute_map.get(attr.name).get_static_value() === attr.value
for (const { name } of roles) { ))
if (name === role) { ) {
return true; for (const name of ax_object) {
} const roles = AXObjectRoles.get(name);
} if (roles) {
} for (const { name } of roles) {
} if (name === role) {
} return true;
} }
return false; }
}
}
}
}
return false;
} }
// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofilling-form-controls:-the-autocomplete-attribute // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofilling-form-controls:-the-autocomplete-attribute
const address_type_tokens = new Set(['shipping', 'billing']); const address_type_tokens = new Set(['shipping', 'billing']);
const autofill_field_name_tokens = new Set([ const autofill_field_name_tokens = new Set([
'', '',
'on', 'on',
'off', 'off',
'name', 'name',
'honorific-prefix', 'honorific-prefix',
'given-name', 'given-name',
'additional-name', 'additional-name',
'family-name', 'family-name',
'honorific-suffix', 'honorific-suffix',
'nickname', 'nickname',
'username', 'username',
'new-password', 'new-password',
'current-password', 'current-password',
'one-time-code', 'one-time-code',
'organization-title', 'organization-title',
'organization', 'organization',
'street-address', 'street-address',
'address-line1', 'address-line1',
'address-line2', 'address-line2',
'address-line3', 'address-line3',
'address-level4', 'address-level4',
'address-level3', 'address-level3',
'address-level2', 'address-level2',
'address-level1', 'address-level1',
'country', 'country',
'country-name', 'country-name',
'postal-code', 'postal-code',
'cc-name', 'cc-name',
'cc-given-name', 'cc-given-name',
'cc-additional-name', 'cc-additional-name',
'cc-family-name', 'cc-family-name',
'cc-number', 'cc-number',
'cc-exp', 'cc-exp',
'cc-exp-month', 'cc-exp-month',
'cc-exp-year', 'cc-exp-year',
'cc-csc', 'cc-csc',
'cc-type', 'cc-type',
'transaction-currency', 'transaction-currency',
'transaction-amount', 'transaction-amount',
'language', 'language',
'bday', 'bday',
'bday-day', 'bday-day',
'bday-month', 'bday-month',
'bday-year', 'bday-year',
'sex', 'sex',
'url', 'url',
'photo' 'photo'
]); ]);
const contact_type_tokens = new Set(['home', 'work', 'mobile', 'fax', 'pager']); const contact_type_tokens = new Set(['home', 'work', 'mobile', 'fax', 'pager']);
const autofill_contact_field_name_tokens = new Set([ const autofill_contact_field_name_tokens = new Set([
'tel', 'tel',
'tel-country-code', 'tel-country-code',
'tel-national', 'tel-national',
'tel-area-code', 'tel-area-code',
'tel-local', 'tel-local',
'tel-local-prefix', 'tel-local-prefix',
'tel-local-suffix', 'tel-local-suffix',
'tel-extension', 'tel-extension',
'email', 'email',
'impp' 'impp'
]); ]);
/** /**
* @param {null | true | string} autocomplete * @param {null | true | string} autocomplete
*/ */
export function is_valid_autocomplete(autocomplete) { export function is_valid_autocomplete(autocomplete) {
if (autocomplete === true) { if (autocomplete === true) {
return false; return false;
} } else if (!autocomplete) {
else if (!autocomplete) { return true; // dynamic value
return true; // dynamic value }
} const tokens = autocomplete.trim().toLowerCase().split(regex_whitespaces);
const tokens = autocomplete.trim().toLowerCase().split(regex_whitespaces); if (typeof tokens[0] === 'string' && tokens[0].startsWith('section-')) {
if (typeof tokens[0] === 'string' && tokens[0].startsWith('section-')) { tokens.shift();
tokens.shift(); }
} if (address_type_tokens.has(tokens[0])) {
if (address_type_tokens.has(tokens[0])) { tokens.shift();
tokens.shift(); }
} if (autofill_field_name_tokens.has(tokens[0])) {
if (autofill_field_name_tokens.has(tokens[0])) { tokens.shift();
tokens.shift(); } else {
} if (contact_type_tokens.has(tokens[0])) {
else { tokens.shift();
if (contact_type_tokens.has(tokens[0])) { }
tokens.shift(); if (autofill_contact_field_name_tokens.has(tokens[0])) {
} tokens.shift();
if (autofill_contact_field_name_tokens.has(tokens[0])) { } else {
tokens.shift(); return false;
} }
else { }
return false; if (tokens[0] === 'webauthn') {
} tokens.shift();
} }
if (tokens[0] === 'webauthn') { return tokens.length === 0;
tokens.shift();
}
return tokens.length === 0;
} }

Loading…
Cancel
Save