Skip to content

Commit cf8a3c0

Browse files
strakerWilcoFiers
andcommitted
fix(target-size): ignore widgets that are inline with other inline elements (#5000)
Closes: #4392 --------- Co-authored-by: Wilco Fiers <WilcoFiers@users.noreply.github.com> Co-authored-by: Wilco Fiers <wilco.fiers@deque.com>
1 parent 3d80a37 commit cf8a3c0

File tree

5 files changed

+337
-135
lines changed

5 files changed

+337
-135
lines changed
Lines changed: 50 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,11 @@
11
import getComposedParent from './get-composed-parent';
22
import sanitize from '../text/sanitize';
3-
import { getNodeFromTree } from '../../core/utils';
3+
import { getNodeFromTree, nodeLookup } from '../../core/utils';
44
import getRoleType from '../aria/get-role-type';
55

6-
function walkDomNode(node, functor) {
7-
if (functor(node.actualNode) !== false) {
8-
node.children.forEach(child => walkDomNode(child, functor));
9-
}
10-
}
6+
const blockLike = ['block', 'list-item', 'table', 'flex', 'grid'];
117

12-
const blockLike = [
13-
'block',
14-
'list-item',
15-
'table',
16-
'flex',
17-
'grid',
18-
'inline-block'
19-
];
20-
21-
function isBlock(elm) {
22-
const display = window.getComputedStyle(elm).getPropertyValue('display');
23-
return blockLike.includes(display) || display.substr(0, 6) === 'table-';
24-
}
25-
26-
function getBlockParent(node) {
27-
// Find the closest parent
28-
let parentBlock = getComposedParent(node);
29-
while (parentBlock && !isBlock(parentBlock)) {
30-
parentBlock = getComposedParent(parentBlock);
31-
}
32-
33-
return getNodeFromTree(parentBlock);
34-
}
8+
const inlineBlockLike = ['inline-block', 'inline-flex', 'inline-grid'];
359

3610
/**
3711
* Determines if an element is within a text block
@@ -41,23 +15,32 @@ function getBlockParent(node) {
4115
* @param {Element} node [description]
4216
* @param {Object} options Optional
4317
* @property {Bool} noLengthCompare
18+
* @property {Bool} includeInlineBlock
4419
* @return {Boolean} [description]
4520
*/
46-
function isInTextBlock(node, options) {
47-
if (isBlock(node)) {
48-
// Ignore if the link is a block
21+
function isInTextBlock(
22+
node,
23+
{ noLengthCompare, includeInlineBlock = false } = {}
24+
) {
25+
const { vNode, domNode } = nodeLookup(node);
26+
if (isBlock(domNode) || (!includeInlineBlock && isInlineBlockLike(vNode))) {
27+
// Ignore if the element is a block
4928
return false;
5029
}
5130

5231
// Find all the text part of the parent block not in a link, and all the text in a link
53-
const virtualParent = getBlockParent(node);
32+
const virtualParent = getBlockParent(domNode);
5433
let parentText = '';
5534
let widgetText = '';
5635
let inBrBlock = 0;
5736

5837
// We want to ignore hidden text, and if br / hr is used, only use the section of the parent
5938
// that has the link we're looking at
6039
walkDomNode(virtualParent, currNode => {
40+
if (currNode === virtualParent.actualNode) {
41+
return true; // Skip the element itself
42+
}
43+
6144
// We're already passed it, skip everything else
6245
if (inBrBlock === 2) {
6346
return false;
@@ -73,11 +56,12 @@ function isInTextBlock(node, options) {
7356
}
7457

7558
const nodeName = (currNode.nodeName || '').toUpperCase();
76-
if (currNode === node) {
59+
if (currNode === domNode) {
7760
inBrBlock = 1;
7861
}
62+
const nodeIsBlock = isBlock(currNode);
7963
// BR and HR elements break the line
80-
if (['BR', 'HR'].includes(nodeName)) {
64+
if (nodeIsBlock || ['BR', 'HR'].includes(nodeName)) {
8165
if (inBrBlock === 0) {
8266
parentText = '';
8367
widgetText = '';
@@ -86,7 +70,9 @@ function isInTextBlock(node, options) {
8670
}
8771

8872
// Don't walk nodes with content not displayed on screen.
89-
} else if (
73+
}
74+
if (
75+
nodeIsBlock ||
9076
currNode.style.display === 'none' ||
9177
currNode.style.overflow === 'hidden' ||
9278
!['', null, 'none'].includes(currNode.style.float) ||
@@ -103,7 +89,7 @@ function isInTextBlock(node, options) {
10389
});
10490

10591
parentText = sanitize(parentText);
106-
if (options?.noLengthCompare) {
92+
if (noLengthCompare) {
10793
return parentText.length !== 0;
10894
}
10995

@@ -112,3 +98,30 @@ function isInTextBlock(node, options) {
11298
}
11399

114100
export default isInTextBlock;
101+
102+
function isBlock(node) {
103+
const { vNode } = nodeLookup(node);
104+
const display = vNode.getComputedStylePropertyValue('display');
105+
return blockLike.includes(display) || display.substr(0, 6) === 'table-';
106+
}
107+
108+
function isInlineBlockLike(vNode) {
109+
const display = vNode.getComputedStylePropertyValue('display');
110+
return inlineBlockLike.includes(display);
111+
}
112+
113+
function walkDomNode(node, functor) {
114+
if (functor(node.actualNode) !== false) {
115+
node.children.forEach(child => walkDomNode(child, functor));
116+
}
117+
}
118+
119+
function getBlockParent(node) {
120+
// Find the closest parent
121+
let parentBlock = getComposedParent(node);
122+
while (parentBlock && !isBlock(parentBlock)) {
123+
parentBlock = getComposedParent(parentBlock);
124+
}
125+
126+
return getNodeFromTree(parentBlock);
127+
}

lib/rules/widget-not-inline-matches.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ const matchesFns = [
1414
(node, vNode) => isFocusable(vNode),
1515
// Skip nested widgets with tabindex=-1
1616
(node, vNode) => isInTabOrder(vNode) || !hasWidgetAncestorInTabOrder(vNode),
17-
node => !isInTextBlock(node, { noLengthCompare: true })
17+
node =>
18+
!isInTextBlock(node, { noLengthCompare: true, includeInlineBlock: true })
1819
];
1920

2021
function isWidgetType(vNode) {

0 commit comments

Comments
 (0)