@markuplint/ml-spec
Version:
Types and schema that specs of the Markup languages for markuplint
80 lines (79 loc) • 2.92 kB
JavaScript
import { isPresentational } from '../specs/is-presentational.js';
import { getComputedRole } from './get-computed-role.js';
import { getExplicitRole } from './get-explicit-role.js';
import { getImplicitRole } from './get-implicit-role.js';
export function hasRequiredOwnedElement(
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
el, specs, version) {
/**
* The element has aria-owns which means it may have owned elements.
*
* THIS CONDITION IS PARTIAL SUPPORT.
*/
if (el.hasAttribute('aria-owns')) {
// FIXME
return true;
}
/**
* Otherwise, traverses descendants to find owned elements.
*/
const computed = getComputedRole(specs, el, version);
if (!computed.role || computed.role.requiredOwnedElements.length === 0) {
return true;
}
for (const expectRole of computed.role.requiredOwnedElements) {
for (const owned of getClosestNonPresentationalDescendants(el, specs, version)) {
if (isRequiredOwnedElement(owned.el, owned.role, expectRole, specs, version)) {
return true;
}
}
}
return false;
}
export function isRequiredOwnedElement(
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
el, role, query, specs, version) {
const [baseRole, owningRole] = query.split(' > ');
if (role?.name !== baseRole) {
return false;
}
if (!owningRole) {
return true;
}
for (const owning of getClosestNonPresentationalDescendants(el, specs, version)) {
if (owning.role?.name === owningRole) {
return true;
}
}
return false;
}
/**
* Gets the list of closest non-presentational descendants.
* ⚠ THE SPECIFICATION HAS AN ISSUE
* that has not decided whether the owned element is a child or a descendant.
*
* @see https://github.com/w3c/aria/issues/1033
* @see https://github.com/w3c/aria/issues/748
* @see https://github.com/w3c/aria/pull/1162
* @see https://github.com/w3c/aria/pull/1213
*
* Currently, this process interprets that as A CHILD
* because it wants to be near to HTML semantics.
* However, the presentational role behaves transparently
* according to the sample code in WAI-ARIA specification.
*/
function getClosestNonPresentationalDescendants(
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
el, specs, version) {
const owned = [];
for (const child of el.children) {
const explicitRole = getExplicitRole(specs, child, version);
const computed = explicitRole.role ? explicitRole : getImplicitRole(specs, child, version);
if (isPresentational(computed.role?.name)) {
owned.push(...getClosestNonPresentationalDescendants(child, specs, version));
continue;
}
owned.push({ ...computed, el: child });
}
return owned;
}