diff --git a/packages/alfa-aria/src/feature.ts b/packages/alfa-aria/src/feature.ts index 886d221cfc..1decca197a 100644 --- a/packages/alfa-aria/src/feature.ts +++ b/packages/alfa-aria/src/feature.ts @@ -10,12 +10,12 @@ import { Scope, Table } from "@siteimprove/alfa-table"; import { Role } from "./role"; const { hasName, isElement } = Element; -const { and, equals, test } = Predicate; +const { and } = Predicate; export class Feature { public static of( name: N, - role: Feature.Aspect> = () => None, + role: Feature.Aspect, [Feature.RoleOptions]> = () => None, attributes: Feature.Aspect> = () => Map.empty(), status: Feature.Status = { obsolete: false } ): Feature { @@ -23,13 +23,13 @@ export class Feature { } private readonly _name: N; - private readonly _role: Feature.Aspect>; + private readonly _role: Feature.Aspect, [Feature.RoleOptions]>; private readonly _attributes: Feature.Aspect>; private readonly _status: Feature.Status; private constructor( name: N, - role: Feature.Aspect>, + role: Feature.Aspect, [Feature.RoleOptions]>, attributes: Feature.Aspect>, status: Feature.Status ) { @@ -43,7 +43,7 @@ export class Feature { return this._name; } - public get role(): Feature.Aspect> { + public get role(): Feature.Aspect, [Feature.RoleOptions]> { return this._role; } @@ -57,12 +57,23 @@ export class Feature { } export namespace Feature { - export type Aspect = Mapper; + export type Aspect = []> = Mapper< + Element, + T, + A + >; export interface Status { readonly obsolete: boolean; } + export interface RoleOptions { + /** + * @internal + */ + readonly allowPresentational?: boolean; + } + const features = Cache.empty>(); export function register( @@ -298,9 +309,9 @@ Feature.register( Feature.register( Namespace.HTML, - Feature.of("img", (element) => + Feature.of("img", (element, { allowPresentational = true }) => Option.of( - element.attribute("alt").some((alt) => alt.value === "") + allowPresentational && element.attribute("alt").some((alt) => alt.value === "") ? "presentation" : "img" ) diff --git a/packages/alfa-aria/src/node.ts b/packages/alfa-aria/src/node.ts index dcd60b761c..4229eb0a1b 100644 --- a/packages/alfa-aria/src/node.ts +++ b/packages/alfa-aria/src/node.ts @@ -240,7 +240,10 @@ export namespace Node { role.some(isPresentational) && !isAllowedPresentational(node) ) { - return Role.from(node, { explicit: false }); + return Role.from(node, { + explicit: false, + allowPresentational: false, + }); } return Branched.of(role); diff --git a/packages/alfa-aria/src/role.ts b/packages/alfa-aria/src/role.ts index 9458ee3dc3..99ca303c6d 100644 --- a/packages/alfa-aria/src/role.ts +++ b/packages/alfa-aria/src/role.ts @@ -268,7 +268,11 @@ export namespace Role { const feature = Feature.lookup(namespace, element.name); return feature.flatMap((feature) => - feature.role(element).flatMap(Role.lookup) + feature + .role(element, { + allowPresentational: options.allowPresentational, + }) + .flatMap(Role.lookup) ); }); } @@ -280,7 +284,7 @@ export namespace Role { } export namespace from { - export interface Options { + export interface Options extends Feature.RoleOptions { readonly explicit?: boolean; readonly implicit?: boolean; } diff --git a/packages/alfa-rules/src/common/applicability/video.ts b/packages/alfa-rules/src/common/applicability/video.ts index 120d5fad8d..f606d71cdc 100644 --- a/packages/alfa-rules/src/common/applicability/video.ts +++ b/packages/alfa-rules/src/common/applicability/video.ts @@ -8,11 +8,10 @@ import { Predicate } from "@siteimprove/alfa-predicate"; import { isVisible } from "../predicate/is-visible"; import { Question } from "../question"; -import { hasAttribute } from "../predicate/has-attribute"; const { isElement, hasName, hasNamespace } = Element; const { filter, map, some } = Iterable; -const { and, equals } = Predicate; +const { and } = Predicate; export function video( document: Document, @@ -39,7 +38,23 @@ export function video( Element.isElement, and( hasName("track"), - hasAttribute("kind", equals(track.kind)) + (trackElement) => + trackElement + .attribute("kind") + // @see https://html.spec.whatwg.org/multipage/media.html#attr-track-kind + .map( + (kind) => + kind + .enumerate( + "subtitles", + "captions", + "descriptions", + "chapters", + "metadata" + ) + .getOr("metadata") // invalid value default + ) + .getOr("subtitles") === track.kind // missing value default ) ) )