From ce87f3db8b6a96714defb4a7047d9fbc268d7332 Mon Sep 17 00:00:00 2001 From: Jean-Yves Moyen Date: Wed, 30 Jun 2021 15:47:01 +0200 Subject: [PATCH 1/7] Streamline isVisible a bit --- packages/alfa-rules/src/common/predicate.ts | 1 + .../common/predicate/has-computed-style.ts | 15 +++ .../src/common/predicate/is-visible.ts | 102 ++++++++---------- packages/alfa-rules/tsconfig.json | 1 + 4 files changed, 62 insertions(+), 57 deletions(-) create mode 100644 packages/alfa-rules/src/common/predicate/has-computed-style.ts diff --git a/packages/alfa-rules/src/common/predicate.ts b/packages/alfa-rules/src/common/predicate.ts index 729f848546..3c4d4347d0 100644 --- a/packages/alfa-rules/src/common/predicate.ts +++ b/packages/alfa-rules/src/common/predicate.ts @@ -4,6 +4,7 @@ export * from "./predicate/has-border"; export * from "./predicate/has-box-shadow"; export * from "./predicate/has-cascaded-value-declared-in-inline-style"; export * from "./predicate/has-child"; +export * from "./predicate/has-computed-style"; export * from "./predicate/has-descendant"; export * from "./predicate/has-explicit-role"; export * from "./predicate/has-heading-level"; diff --git a/packages/alfa-rules/src/common/predicate/has-computed-style.ts b/packages/alfa-rules/src/common/predicate/has-computed-style.ts new file mode 100644 index 0000000000..0a4dc17593 --- /dev/null +++ b/packages/alfa-rules/src/common/predicate/has-computed-style.ts @@ -0,0 +1,15 @@ +import { Device } from "@siteimprove/alfa-device"; +import { Element } from "@siteimprove/alfa-dom"; +import { Predicate } from "@siteimprove/alfa-predicate"; +import { Context } from "@siteimprove/alfa-selector"; +import { Property, Style } from "@siteimprove/alfa-style"; + +export function hasComputedStyle( + name: N, + predicate: Predicate>, + device: Device, + context?: Context +): Predicate { + return (element) => + Style.from(element, device, context).computed(name).some(predicate); +} diff --git a/packages/alfa-rules/src/common/predicate/is-visible.ts b/packages/alfa-rules/src/common/predicate/is-visible.ts index f0e3f77a1b..9f7b7586a2 100644 --- a/packages/alfa-rules/src/common/predicate/is-visible.ts +++ b/packages/alfa-rules/src/common/predicate/is-visible.ts @@ -1,12 +1,11 @@ import { Device } from "@siteimprove/alfa-device"; import { Element, Text, Node } from "@siteimprove/alfa-dom"; -import { Iterable } from "@siteimprove/alfa-iterable"; import { Predicate } from "@siteimprove/alfa-predicate"; import { Refinement } from "@siteimprove/alfa-refinement"; import { Context } from "@siteimprove/alfa-selector"; -import { Style } from "@siteimprove/alfa-style"; import { + hasComputedStyle, isClipped, isOffscreen, isRendered, @@ -14,70 +13,59 @@ import { isTransparent, } from "../predicate"; -const { every } = Iterable; -const { not } = Predicate; -const { and, or } = Refinement; +const { nor, not, or } = Predicate; +const { and } = Refinement; const { hasName, isElement } = Element; const { isText } = Text; +/** + * Checks if a node is visible + */ export function isVisible(device: Device, context?: Context): Predicate { - return and( - isRendered(device, context), - not(isTransparent(device, context)), - not( - and( - or(isElement, isText), - or(isClipped(device, context), isOffscreen(device, context)) + return not(isInvisible(device, context)); +} + +function isInvisible(device: Device, context?: Context): Predicate { + return or( + not(isRendered(device, context)), + isTransparent(device, context), + isClipped(device, context), + isOffscreen(device, context), + and(isText, (text) => text.data.trim() === ""), + and(isText, (text) => + text + .parent({ flattened: true }) + .filter(isElement) + .some( + hasComputedStyle( + "font-size", + (size) => size.value === 0, + device, + context + ) + ) + ), + and( + isElement, + hasComputedStyle( + "visibility", + (visibility) => visibility.value !== "visible", + device, + context ) ), - (node) => { - if ( - isElement(node) && - Style.from(node, device, context) - .computed("visibility") - .some((visibility) => visibility.value !== "visible") - ) { - return false; - } - - if (isText(node)) { - if (node.data.trim() === "") { - return false; - } - - if ( - node - .parent({ - flattened: true, - }) - .filter(isElement) - .some((element) => - Style.from(element, device, context) - .computed("font-size") - .some((size) => size.value === 0) - ) - ) { - return false; - } - } - - return true; - }, // Most non-replaced elements with no visible children are not visible while // replaced elements are assumed to be replaced by something visible. Some // non-replaced elements are, however, visible even when empty. - not( - and( - isElement, - and(not(or(isReplaced, isVisibleWhenEmpty)), (element) => - every( - element.children({ - nested: true, - flattened: true, - }), - not(isVisible(device, context)) - ) - ) + and( + isElement, + and(nor(isReplaced, isVisibleWhenEmpty), (element) => + element + .children({ + nested: true, + flattened: true, + }) + .every(isInvisible(device, context)) ) ) ); diff --git a/packages/alfa-rules/tsconfig.json b/packages/alfa-rules/tsconfig.json index 746cecb88c..49a1867d77 100644 --- a/packages/alfa-rules/tsconfig.json +++ b/packages/alfa-rules/tsconfig.json @@ -28,6 +28,7 @@ "src/common/predicate/has-box-shadow.ts", "src/common/predicate/has-cascaded-value-declared-in-inline-style.ts", "src/common/predicate/has-child.ts", + "src/common/predicate/has-computed-style.ts", "src/common/predicate/has-descendant.ts", "src/common/predicate/has-explicit-role.ts", "src/common/predicate/has-heading-level.ts", From 59f0efb88f6e2ada40cea8d1d8b482812168fe89 Mon Sep 17 00:00:00 2001 From: Jean-Yves Moyen Date: Wed, 30 Jun 2021 16:47:52 +0200 Subject: [PATCH 2/7] Consider nodes with a clipped ancestor as clipped --- .../src/common/predicate/is-clipped.ts | 107 +++++++++--------- 1 file changed, 55 insertions(+), 52 deletions(-) diff --git a/packages/alfa-rules/src/common/predicate/is-clipped.ts b/packages/alfa-rules/src/common/predicate/is-clipped.ts index 71ed96dc26..308e2c566a 100644 --- a/packages/alfa-rules/src/common/predicate/is-clipped.ts +++ b/packages/alfa-rules/src/common/predicate/is-clipped.ts @@ -1,6 +1,6 @@ import { Cache } from "@siteimprove/alfa-cache"; import { Device } from "@siteimprove/alfa-device"; -import { Element, Text, Node } from "@siteimprove/alfa-dom"; +import { Element, Node } from "@siteimprove/alfa-dom"; import { Predicate } from "@siteimprove/alfa-predicate"; import { Context } from "@siteimprove/alfa-selector"; import { Style } from "@siteimprove/alfa-style"; @@ -8,7 +8,6 @@ import { Style } from "@siteimprove/alfa-style"; const { abs } = Math; const { isElement } = Element; const { or, test } = Predicate; -const { isText } = Text; const cache = Cache.empty>>(); @@ -24,13 +23,25 @@ export function isClipped( test( or( isClippedBySize(device, context), - isClippedByMasking(device, context) + isClippedByIndent(device, context), + isClippedByMasking(device, context), + (node) => + node + .parent({ + flattened: true, + nested: true, + }) + .some(isClipped(device, context)) ), node ) ); } +/** + * Checks if an element's size is reduced to 0 or 1 pixel, and overflow is + * somehow hidden. + */ function isClippedBySize( device: Device, context: Context = Context.empty() @@ -39,13 +50,19 @@ function isClippedBySize( if (isElement(node)) { const style = Style.from(node, device, context); - const { value: x } = style.computed("overflow-x"); - const { value: y } = style.computed("overflow-y"); + const { + value: { value: x }, + } = style.computed("overflow-x"); + const { + value: { value: y }, + } = style.computed("overflow-y"); const { value: height } = style.computed("height"); const { value: width } = style.computed("width"); - if (x.value === "hidden" || y.value === "hidden") { + const hasNoScrollBar = x === "hidden" || y === "hidden"; + + if (x !== "visible" || y !== "visible") { for (const dimension of [height, width]) { switch (dimension.type) { case "percentage": @@ -55,8 +72,12 @@ function isClippedBySize( break; } + // technically, 1×1 elements are (possibly) visible since they + // show one pixel of background. We assume this is used to hide + // elements and that the background is the same as the surrounding + // one. case "length": - if (dimension.value <= 1) { + if (dimension.value <= (hasNoScrollBar ? 1 : 0)) { return true; } else { break; @@ -64,24 +85,37 @@ function isClippedBySize( } } } + } - if ( - x.value === "auto" || - x.value === "scroll" || - y.value === "auto" || - y.value === "scroll" - ) { - for (const dimension of [height, width]) { - switch (dimension.type) { + return false; + }; +} + +/** + * Checks if an element is fully indented out of screen. + */ +function isClippedByIndent(device: Device, context: Context): Predicate { + return function isClipped(node: Node): boolean { + if (isElement(node)) { + const style = Style.from(node, device, context); + + const { value: x } = style.computed("overflow-x"); + + if (x.value === "hidden") { + const { value: indent } = style.computed("text-indent"); + const { value: whitespace } = style.computed("white-space"); + + if (indent.value < 0 || whitespace.value === "nowrap") { + switch (indent.type) { case "percentage": - if (dimension.value <= 0) { + if (abs(indent.value) >= 1) { return true; } else { break; } case "length": - if (dimension.value <= 0) { + if (abs(indent.value) >= 999) { return true; } else { break; @@ -91,39 +125,13 @@ function isClippedBySize( } } - for (const parent of node.parent({ flattened: true })) { - if (isText(node) && isElement(parent)) { - const style = Style.from(parent, device, context); - - const { value: x } = style.computed("overflow-x"); - - if (x.value === "hidden") { - const { value: indent } = style.computed("text-indent"); - const { value: whitespace } = style.computed("white-space"); - - if (indent.value < 0 || whitespace.value === "nowrap") { - switch (indent.type) { - case "percentage": - if (abs(indent.value) >= 1) { - return true; - } - - case "length": - if (abs(indent.value) >= 999) { - return true; - } - } - } - } - } - - return isClipped(parent); - } - return false; }; } +/** + * Checks if an element is fully masked by a clipping shape. + */ function isClippedByMasking(device: Device, context: Context): Predicate { return function isClipped(node: Node): boolean { if (isElement(node)) { @@ -144,11 +152,6 @@ function isClippedByMasking(device: Device, context: Context): Predicate { } } - return node - .parent({ - flattened: true, - nested: true, - }) - .some(isClipped); + return false; }; } From 3d5aea4bbd4834dbd2a3d693a3e3373c485f23f1 Mon Sep 17 00:00:00 2001 From: Jean-Yves Moyen Date: Wed, 30 Jun 2021 16:53:32 +0200 Subject: [PATCH 3/7] Clean up code --- .../src/common/predicate/is-clipped.ts | 183 +++++++++--------- 1 file changed, 93 insertions(+), 90 deletions(-) diff --git a/packages/alfa-rules/src/common/predicate/is-clipped.ts b/packages/alfa-rules/src/common/predicate/is-clipped.ts index 308e2c566a..c8ef5d90d2 100644 --- a/packages/alfa-rules/src/common/predicate/is-clipped.ts +++ b/packages/alfa-rules/src/common/predicate/is-clipped.ts @@ -2,12 +2,14 @@ import { Cache } from "@siteimprove/alfa-cache"; import { Device } from "@siteimprove/alfa-device"; import { Element, Node } from "@siteimprove/alfa-dom"; import { Predicate } from "@siteimprove/alfa-predicate"; +import { Refinement } from "@siteimprove/alfa-refinement"; import { Context } from "@siteimprove/alfa-selector"; import { Style } from "@siteimprove/alfa-style"; const { abs } = Math; const { isElement } = Element; const { or, test } = Predicate; +const { and } = Refinement; const cache = Cache.empty>>(); @@ -22,10 +24,15 @@ export function isClipped( .get(node, () => test( or( - isClippedBySize(device, context), - isClippedByIndent(device, context), - isClippedByMasking(device, context), - (node) => + and( + isElement, + or( + isClippedBySize(device, context), + isClippedByIndent(device, context), + isClippedByMasking(device, context) + ) + ), + (node: Node) => node .parent({ flattened: true, @@ -45,44 +52,42 @@ export function isClipped( function isClippedBySize( device: Device, context: Context = Context.empty() -): Predicate { - return function isClipped(node): boolean { - if (isElement(node)) { - const style = Style.from(node, device, context); - - const { - value: { value: x }, - } = style.computed("overflow-x"); - const { - value: { value: y }, - } = style.computed("overflow-y"); - - const { value: height } = style.computed("height"); - const { value: width } = style.computed("width"); - - const hasNoScrollBar = x === "hidden" || y === "hidden"; - - if (x !== "visible" || y !== "visible") { - for (const dimension of [height, width]) { - switch (dimension.type) { - case "percentage": - if (dimension.value <= 0) { - return true; - } else { - break; - } - - // technically, 1×1 elements are (possibly) visible since they - // show one pixel of background. We assume this is used to hide - // elements and that the background is the same as the surrounding - // one. - case "length": - if (dimension.value <= (hasNoScrollBar ? 1 : 0)) { - return true; - } else { - break; - } - } +): Predicate { + return function isClipped(element: Element): boolean { + const style = Style.from(element, device, context); + + const { + value: { value: x }, + } = style.computed("overflow-x"); + const { + value: { value: y }, + } = style.computed("overflow-y"); + + const { value: height } = style.computed("height"); + const { value: width } = style.computed("width"); + + const hasNoScrollBar = x === "hidden" || y === "hidden"; + + if (x !== "visible" || y !== "visible") { + for (const dimension of [height, width]) { + switch (dimension.type) { + case "percentage": + if (dimension.value <= 0) { + return true; + } else { + break; + } + + // technically, 1×1 elements are (possibly) visible since they + // show one pixel of background. We assume this is used to hide + // elements and that the background is the same as the surrounding + // one. + case "length": + if (dimension.value <= (hasNoScrollBar ? 1 : 0)) { + return true; + } else { + break; + } } } } @@ -94,33 +99,34 @@ function isClippedBySize( /** * Checks if an element is fully indented out of screen. */ -function isClippedByIndent(device: Device, context: Context): Predicate { - return function isClipped(node: Node): boolean { - if (isElement(node)) { - const style = Style.from(node, device, context); - - const { value: x } = style.computed("overflow-x"); - - if (x.value === "hidden") { - const { value: indent } = style.computed("text-indent"); - const { value: whitespace } = style.computed("white-space"); - - if (indent.value < 0 || whitespace.value === "nowrap") { - switch (indent.type) { - case "percentage": - if (abs(indent.value) >= 1) { - return true; - } else { - break; - } - - case "length": - if (abs(indent.value) >= 999) { - return true; - } else { - break; - } - } +function isClippedByIndent( + device: Device, + context: Context +): Predicate { + return function isClipped(element: Element): boolean { + const style = Style.from(element, device, context); + + const { value: x } = style.computed("overflow-x"); + + if (x.value === "hidden") { + const { value: indent } = style.computed("text-indent"); + const { value: whitespace } = style.computed("white-space"); + + if (indent.value < 0 || whitespace.value === "nowrap") { + switch (indent.type) { + case "percentage": + if (abs(indent.value) >= 1) { + return true; + } else { + break; + } + + case "length": + if (abs(indent.value) >= 999) { + return true; + } else { + break; + } } } } @@ -132,26 +138,23 @@ function isClippedByIndent(device: Device, context: Context): Predicate { /** * Checks if an element is fully masked by a clipping shape. */ -function isClippedByMasking(device: Device, context: Context): Predicate { - return function isClipped(node: Node): boolean { - if (isElement(node)) { - const style = Style.from(node, device, context); - - const { value: clip } = style.computed("clip"); - const { value: position } = style.computed("position"); - - if ( - (position.value === "absolute" || position.value === "fixed") && - clip.type === "shape" && - ((clip.shape.top.type === "length" && - clip.shape.top.equals(clip.shape.bottom)) || - (clip.shape.left.type === "length" && - clip.shape.top.equals(clip.shape.right))) - ) { - return true; - } - } - - return false; +function isClippedByMasking( + device: Device, + context: Context +): Predicate { + return function isClipped(element: Element): boolean { + const style = Style.from(element, device, context); + + const { value: clip } = style.computed("clip"); + const { value: position } = style.computed("position"); + + return ( + (position.value === "absolute" || position.value === "fixed") && + clip.type === "shape" && + ((clip.shape.top.type === "length" && + clip.shape.top.equals(clip.shape.bottom)) || + (clip.shape.left.type === "length" && + clip.shape.top.equals(clip.shape.right))) + ); }; } From 898ccbab09ba1f5f02b88ce573eea72e02601c3f Mon Sep 17 00:00:00 2001 From: Jean-Yves Moyen Date: Thu, 1 Jul 2021 10:08:19 +0200 Subject: [PATCH 4/7] Add tests --- .../test/common/predicate/is-visible.spec.tsx | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/packages/alfa-rules/test/common/predicate/is-visible.spec.tsx b/packages/alfa-rules/test/common/predicate/is-visible.spec.tsx index 635f106706..d2224a2e1d 100644 --- a/packages/alfa-rules/test/common/predicate/is-visible.spec.tsx +++ b/packages/alfa-rules/test/common/predicate/is-visible.spec.tsx @@ -418,3 +418,27 @@ test(`isVisible() returns false for a text node with a parent element with t.equal(isVisible(text), false); t.equal(isVisible(element), false); }); + +test(`isVisible() returns false for an element with a fully clipped ancestor`, (t) => { + const spanSize = Hello World; + const spanIndent = Hello World; + const spanMask = Hello World; + + h.document([ +
+ {spanSize} +
, +
+ {spanIndent} +
, +
+ {spanMask} +
, + ]); + + for (const target of [spanSize, spanIndent, spanMask]) { + t.equal(isVisible(target), false); + } +}); From 0b65f47453948011a9489a2cf5f124536fd27a73 Mon Sep 17 00:00:00 2001 From: Jean-Yves Moyen Date: Thu, 1 Jul 2021 10:35:17 +0200 Subject: [PATCH 5/7] Curryfy hasComputedStyle --- .../common/predicate/has-computed-style.ts | 25 ++++++++++---- .../src/common/predicate/is-clipped.ts | 9 ++++- .../src/common/predicate/is-visible.ts | 34 +++++++++---------- 3 files changed, 43 insertions(+), 25 deletions(-) diff --git a/packages/alfa-rules/src/common/predicate/has-computed-style.ts b/packages/alfa-rules/src/common/predicate/has-computed-style.ts index 0a4dc17593..e8dae7ba37 100644 --- a/packages/alfa-rules/src/common/predicate/has-computed-style.ts +++ b/packages/alfa-rules/src/common/predicate/has-computed-style.ts @@ -1,15 +1,28 @@ import { Device } from "@siteimprove/alfa-device"; -import { Element } from "@siteimprove/alfa-dom"; +import { Element, Text } from "@siteimprove/alfa-dom"; import { Predicate } from "@siteimprove/alfa-predicate"; import { Context } from "@siteimprove/alfa-selector"; import { Property, Style } from "@siteimprove/alfa-style"; +const { isElement } = Element; + export function hasComputedStyle( - name: N, - predicate: Predicate>, device: Device, context?: Context -): Predicate { - return (element) => - Style.from(element, device, context).computed(name).some(predicate); +): ( + name: N, + predicate: Predicate> +) => Predicate { + return function hasComputedStyle( + name: N, + predicate: Predicate> + ): Predicate { + return (node) => + isElement(node) + ? Style.from(node, device, context).computed(name).some(predicate) + : node + .parent({ flattened: true }) + .filter(isElement) + .some(hasComputedStyle(name, predicate)); + }; } diff --git a/packages/alfa-rules/src/common/predicate/is-clipped.ts b/packages/alfa-rules/src/common/predicate/is-clipped.ts index c8ef5d90d2..415e71b742 100644 --- a/packages/alfa-rules/src/common/predicate/is-clipped.ts +++ b/packages/alfa-rules/src/common/predicate/is-clipped.ts @@ -13,6 +13,9 @@ const { and } = Refinement; const cache = Cache.empty>>(); +/** + * Checks if a node (or one of its ancestor) is fully clipped + */ export function isClipped( device: Device, context: Context = Context.empty() @@ -24,6 +27,7 @@ export function isClipped( .get(node, () => test( or( + // Either it a clipped element and( isElement, or( @@ -32,6 +36,7 @@ export function isClipped( isClippedByMasking(device, context) ) ), + // Or its parent is clipped (node: Node) => node .parent({ @@ -46,6 +51,7 @@ export function isClipped( } /** + * @internal * Checks if an element's size is reduced to 0 or 1 pixel, and overflow is * somehow hidden. */ @@ -97,7 +103,8 @@ function isClippedBySize( } /** - * Checks if an element is fully indented out of screen. + * @internal + * Checks if an element is fully indented out of its box. */ function isClippedByIndent( device: Device, diff --git a/packages/alfa-rules/src/common/predicate/is-visible.ts b/packages/alfa-rules/src/common/predicate/is-visible.ts index 9f7b7586a2..09ac4ca6c5 100644 --- a/packages/alfa-rules/src/common/predicate/is-visible.ts +++ b/packages/alfa-rules/src/common/predicate/is-visible.ts @@ -3,6 +3,7 @@ import { Element, Text, Node } from "@siteimprove/alfa-dom"; import { Predicate } from "@siteimprove/alfa-predicate"; import { Refinement } from "@siteimprove/alfa-refinement"; import { Context } from "@siteimprove/alfa-selector"; +import { Property, Style } from "@siteimprove/alfa-style"; import { hasComputedStyle, @@ -25,34 +26,31 @@ export function isVisible(device: Device, context?: Context): Predicate { return not(isInvisible(device, context)); } +/** + * @internal + */ function isInvisible(device: Device, context?: Context): Predicate { + const hasStyle = ( + name: N, + predicate: Predicate> + ) => hasComputedStyle(device, context)(name, predicate); + return or( not(isRendered(device, context)), isTransparent(device, context), isClipped(device, context), isOffscreen(device, context), + // Empty text and(isText, (text) => text.data.trim() === ""), - and(isText, (text) => - text - .parent({ flattened: true }) - .filter(isElement) - .some( - hasComputedStyle( - "font-size", - (size) => size.value === 0, - device, - context - ) - ) + // Text of size 0 + and( + isText, + hasStyle("font-size", (size) => size.value === 0) ), + // Element with visibility ≠ "visible" and( isElement, - hasComputedStyle( - "visibility", - (visibility) => visibility.value !== "visible", - device, - context - ) + hasStyle("visibility", (visibility) => visibility.value !== "visible") ), // Most non-replaced elements with no visible children are not visible while // replaced elements are assumed to be replaced by something visible. Some From 0df15913862610d4746ebd0b0713563e4e116627 Mon Sep 17 00:00:00 2001 From: Jean-Yves Moyen Date: Thu, 1 Jul 2021 13:39:12 +0200 Subject: [PATCH 6/7] Apply suggestions from code review Co-authored-by: Kasper Isager --- packages/alfa-rules/src/common/predicate/is-clipped.ts | 4 +--- packages/alfa-rules/src/common/predicate/is-visible.ts | 5 +---- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/alfa-rules/src/common/predicate/is-clipped.ts b/packages/alfa-rules/src/common/predicate/is-clipped.ts index 415e71b742..19c59d0b43 100644 --- a/packages/alfa-rules/src/common/predicate/is-clipped.ts +++ b/packages/alfa-rules/src/common/predicate/is-clipped.ts @@ -51,7 +51,6 @@ export function isClipped( } /** - * @internal * Checks if an element's size is reduced to 0 or 1 pixel, and overflow is * somehow hidden. */ @@ -84,7 +83,7 @@ function isClippedBySize( break; } - // technically, 1×1 elements are (possibly) visible since they + // Technically, 1×1 elements are (possibly) visible since they // show one pixel of background. We assume this is used to hide // elements and that the background is the same as the surrounding // one. @@ -103,7 +102,6 @@ function isClippedBySize( } /** - * @internal * Checks if an element is fully indented out of its box. */ function isClippedByIndent( diff --git a/packages/alfa-rules/src/common/predicate/is-visible.ts b/packages/alfa-rules/src/common/predicate/is-visible.ts index 09ac4ca6c5..e8234cca9b 100644 --- a/packages/alfa-rules/src/common/predicate/is-visible.ts +++ b/packages/alfa-rules/src/common/predicate/is-visible.ts @@ -26,9 +26,6 @@ export function isVisible(device: Device, context?: Context): Predicate { return not(isInvisible(device, context)); } -/** - * @internal - */ function isInvisible(device: Device, context?: Context): Predicate { const hasStyle = ( name: N, @@ -47,7 +44,7 @@ function isInvisible(device: Device, context?: Context): Predicate { isText, hasStyle("font-size", (size) => size.value === 0) ), - // Element with visibility ≠ "visible" + // Element with visibility != "visible" and( isElement, hasStyle("visibility", (visibility) => visibility.value !== "visible") From cd224b1b9671080ad6a3aed6915566d3a37fa08d Mon Sep 17 00:00:00 2001 From: Jean-Yves Moyen Date: Thu, 1 Jul 2021 13:44:34 +0200 Subject: [PATCH 7/7] Uncurryfy hasComputedStyle --- .../common/predicate/has-computed-style.ts | 25 ++++++++----------- .../src/common/predicate/is-visible.ts | 14 +++++------ 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/packages/alfa-rules/src/common/predicate/has-computed-style.ts b/packages/alfa-rules/src/common/predicate/has-computed-style.ts index e8dae7ba37..1a14bdacfd 100644 --- a/packages/alfa-rules/src/common/predicate/has-computed-style.ts +++ b/packages/alfa-rules/src/common/predicate/has-computed-style.ts @@ -7,22 +7,17 @@ import { Property, Style } from "@siteimprove/alfa-style"; const { isElement } = Element; export function hasComputedStyle( + name: N, + predicate: Predicate>, device: Device, context?: Context -): ( - name: N, - predicate: Predicate> -) => Predicate { - return function hasComputedStyle( - name: N, - predicate: Predicate> - ): Predicate { - return (node) => - isElement(node) - ? Style.from(node, device, context).computed(name).some(predicate) - : node - .parent({ flattened: true }) - .filter(isElement) - .some(hasComputedStyle(name, predicate)); +): Predicate { + return function hasComputedStyle(node): boolean { + return isElement(node) + ? Style.from(node, device, context).computed(name).some(predicate) + : node + .parent({ flattened: true }) + .filter(isElement) + .some(hasComputedStyle); }; } diff --git a/packages/alfa-rules/src/common/predicate/is-visible.ts b/packages/alfa-rules/src/common/predicate/is-visible.ts index e8234cca9b..11843e2fcf 100644 --- a/packages/alfa-rules/src/common/predicate/is-visible.ts +++ b/packages/alfa-rules/src/common/predicate/is-visible.ts @@ -27,11 +27,6 @@ export function isVisible(device: Device, context?: Context): Predicate { } function isInvisible(device: Device, context?: Context): Predicate { - const hasStyle = ( - name: N, - predicate: Predicate> - ) => hasComputedStyle(device, context)(name, predicate); - return or( not(isRendered(device, context)), isTransparent(device, context), @@ -42,12 +37,17 @@ function isInvisible(device: Device, context?: Context): Predicate { // Text of size 0 and( isText, - hasStyle("font-size", (size) => size.value === 0) + hasComputedStyle("font-size", (size) => size.value === 0, device, context) ), // Element with visibility != "visible" and( isElement, - hasStyle("visibility", (visibility) => visibility.value !== "visible") + hasComputedStyle( + "visibility", + (visibility) => visibility.value !== "visible", + device, + context + ) ), // Most non-replaced elements with no visible children are not visible while // replaced elements are assumed to be replaced by something visible. Some