Commit ae20335f authored by David Sveningsson's avatar David Sveningsson
Browse files

feat(rules): new helper `isHTMLHidden`

checks if node or ancestor node have the HTML `hidden` attribute
parent 0869de3c
......@@ -2,7 +2,7 @@ import { Config } from "../../config";
import { DOMTree } from "../../dom";
import { Parser } from "../../parser";
import { processAttribute } from "../../transform/mocks/attribute";
import { inAccessibilityTree, isAriaHidden, isPresentation } from "./a17y";
import { inAccessibilityTree, isAriaHidden, isHTMLHidden, isPresentation } from "./a17y";
describe("a17y helpers", () => {
let parser: Parser;
......@@ -117,6 +117,55 @@ describe("a17y helpers", () => {
});
});
describe("isHTMLHidden()", () => {
it("should return false if node is missing hidden", () => {
expect.assertions(1);
const root = parse("<p>Lorem ipsum</p>");
const p = root.querySelector("p");
expect(isHTMLHidden(p)).toBeFalsy();
});
it("should return false if ancestors are missing hidden", () => {
expect.assertions(1);
const root = parse("<div><p>Lorem ipsum</p></div>");
const p = root.querySelector("p");
expect(isHTMLHidden(p)).toBeFalsy();
});
it("should return false if node has dynamic hidden", () => {
expect.assertions(1);
const root = parse('<p dynamic-hidden="variable">Lorem ipsum</p>');
const p = root.querySelector("p");
expect(isHTMLHidden(p)).toBeFalsy();
});
it("should return true if node has hidden", () => {
expect.assertions(1);
const root = parse("<p hidden>Lorem ipsum</p>");
const p = root.querySelector("p");
expect(isHTMLHidden(p)).toBeTruthy();
});
it("should return true if ancestor has hidden", () => {
expect.assertions(1);
const root = parse("<div hidden><p>Lorem ipsum</p></div>");
const p = root.querySelector("p");
expect(isHTMLHidden(p)).toBeTruthy();
});
it("should cache result", () => {
expect.assertions(4);
const root = parse("<p hidden></p>");
const p = root.querySelector("p");
const spy = jest.spyOn(p, "getAttribute");
expect(isHTMLHidden(p)).toBeTruthy();
expect(spy).toHaveBeenCalledTimes(1);
spy.mockClear();
expect(isHTMLHidden(p)).toBeTruthy();
expect(spy).toHaveBeenCalledTimes(0);
});
});
describe("isPresentation()", () => {
it("should return false if node is missing role", () => {
expect.assertions(1);
......
......@@ -3,11 +3,13 @@ import { HtmlElement } from "../../dom";
declare module "../../dom/cache" {
export interface DOMNodeCache {
[ARIA_HIDDEN_CACHE]: boolean;
[HTML_HIDDEN_CACHE]: boolean;
[ROLE_PRESENTATION_CACHE]: boolean;
}
}
const ARIA_HIDDEN_CACHE = Symbol(isAriaHidden.name);
const HTML_HIDDEN_CACHE = Symbol(isHTMLHidden.name);
const ROLE_PRESENTATION_CACHE = Symbol(isPresentation.name);
/**
......@@ -48,6 +50,33 @@ export function isAriaHidden(node: HtmlElement): boolean {
return node.cacheSet(ARIA_HIDDEN_CACHE, false);
}
/**
* Tests if this element or an ancestor have `hidden` attribute.
*
* Dynamic values yields `false` since the element will conditionally be in the
* DOM tree and must fulfill it's conditions.
*/
export function isHTMLHidden(node: HtmlElement): boolean {
if (node.cacheExists(HTML_HIDDEN_CACHE)) {
return node.cacheGet(HTML_HIDDEN_CACHE);
}
let cur: HtmlElement = node;
do {
const hidden = cur.getAttribute("hidden");
/* hidden present */
if (hidden !== null && hidden.isStatic) {
return cur.cacheSet(HTML_HIDDEN_CACHE, true);
}
/* check parents */
cur = cur.parent;
} while (!cur.isRootElement());
return node.cacheSet(HTML_HIDDEN_CACHE, false);
}
/**
* Tests if this element or a parent element has role="presentation".
*
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment