From a125f792b91c782ff7847cc13ea4dab3874713c5 Mon Sep 17 00:00:00 2001
From: Wilco Fiers
Date: Wed, 19 Jul 2017 17:20:33 +0200
Subject: [PATCH] fix(is-in-text-block): Add Shadow DOM support
---
lib/commons/dom/is-in-text-block.js | 50 +++++++--------
test/commons/dom/is-in-text-block.js | 94 +++++++++++++++++++---------
2 files changed, 87 insertions(+), 57 deletions(-)
diff --git a/lib/commons/dom/is-in-text-block.js b/lib/commons/dom/is-in-text-block.js
index 97c53d0fbd..a6f867b508 100644
--- a/lib/commons/dom/is-in-text-block.js
+++ b/lib/commons/dom/is-in-text-block.js
@@ -1,48 +1,42 @@
/* global axe, dom, window */
-
function walkDomNode(node, functor) {
- 'use strict';
- var shouldWalk = functor(node);
- node = node.firstChild;
- while (node) {
- if (shouldWalk !== false) {
- walkDomNode(node, functor);
- }
- node = node.nextSibling;
- }
+ if (functor(node.actualNode) !== false) {
+ node.children.forEach(child => walkDomNode(child, functor));
+ }
}
var blockLike = ['block', 'list-item', 'table', 'flex', 'grid', 'inline-block'];
function isBlock(elm) {
- 'use strict';
var display = window.getComputedStyle(elm).getPropertyValue('display');
- return (blockLike.indexOf(display) !== -1 ||
- display.substr(0, 6) === 'table-');
+ return (blockLike.includes(display) || display.substr(0, 6) === 'table-');
+}
+
+function getBlockParent (node) {
+ // Find the closest parent
+ let parentBlock = dom.getComposedParent(node);
+ while (parentBlock && !isBlock(parentBlock)) {
+ parentBlock = dom.getComposedParent(parentBlock);
+ }
+ return axe.utils.getNodeFromTree(axe._tree[0], parentBlock);
}
dom.isInTextBlock = function isInTextBlock(node) {
// jshint maxcomplexity: 15
- 'use strict';
- // Ignore if the link is a block
if (isBlock(node)) {
+ // Ignore if the link is a block
return false;
}
- // Find the closest parent
- var parentBlock = node.parentNode;
- while (parentBlock.nodeType === 1 && !isBlock(parentBlock)) {
- parentBlock = parentBlock.parentNode;
- }
-
// Find all the text part of the parent block not in a link, and all the text in a link
- var parentText = '';
- var linkText = '';
- var inBrBlock = 0;
+ const virtualParent = getBlockParent(node);
+ let parentText = '';
+ let linkText = '';
+ let inBrBlock = 0;
// We want to ignore hidden text, and if br / hr is used, only use the section of the parent
// that has the link we're looking at
- walkDomNode(parentBlock, function (currNode) {
+ walkDomNode(virtualParent, function (currNode) {
// We're already passed it, skip everything else
if (inBrBlock === 2) {
return false;
@@ -59,7 +53,7 @@ dom.isInTextBlock = function isInTextBlock(node) {
var nodeName = (currNode.nodeName || '').toUpperCase();
// BR and HR elements break the line
- if (['BR', 'HR'].indexOf(nodeName) !== -1) {
+ if (['BR', 'HR'].includes(nodeName)) {
if (inBrBlock === 0) {
parentText = '';
linkText = '';
@@ -70,8 +64,8 @@ dom.isInTextBlock = function isInTextBlock(node) {
// Don't walk nodes with content not displayed on screen.
} else if (currNode.style.display === 'none' ||
currNode.style.overflow === 'hidden' ||
- ['', null, 'none'].indexOf(currNode.style.float) === -1 ||
- ['', null, 'relative'].indexOf(currNode.style.position) === -1) {
+ !['', null, 'none'].includes(currNode.style.float) ||
+ !['', null, 'relative'].includes(currNode.style.position)) {
return false;
// Don't walk links, we're only interested in what's not in them.
diff --git a/test/commons/dom/is-in-text-block.js b/test/commons/dom/is-in-text-block.js
index 6c779b2e9d..f89519bb19 100644
--- a/test/commons/dom/is-in-text-block.js
+++ b/test/commons/dom/is-in-text-block.js
@@ -2,143 +2,179 @@ describe('dom.isInTextBlock', function () {
'use strict';
var fixture = document.getElementById('fixture');
+ var shadowSupport = axe.testUtils.shadowSupport;
+ var fixtureSetup = axe.testUtils.fixtureSetup;
afterEach(function () {
fixture.innerHTML = '';
});
it('returns true if the element is a node in a block of text', function () {
- fixture.innerHTML =
+ fixtureSetup(
'Some paragraph with text ' +
' link' +
- '
';
+ '
');
var link = document.getElementById('link');
assert.isTrue(axe.commons.dom.isInTextBlock(link));
});
it('returns false if the element is a block', function () {
- fixture.innerHTML =
+ fixtureSetup(
'Some paragraph with text ' +
' link' +
- '
';
+ '');
var link = document.getElementById('link');
assert.isFalse(axe.commons.dom.isInTextBlock(link));
});
it('returns false if the element has the only text in the block', function () {
- fixture.innerHTML =
+ fixtureSetup(
'' +
' link'+
- '
';
+ '');
var link = document.getElementById('link');
assert.isFalse(axe.commons.dom.isInTextBlock(link));
});
it('returns false if there is more text in link(s) than in the rest of the block', function () {
- fixture.innerHTML =
+ fixtureSetup(
' short text:' +
' on a link with a very long text' +
- '
';
+ '');
var link = document.getElementById('link');
assert.isFalse(axe.commons.dom.isInTextBlock(link));
});
it('return false if there are links along side other links', function () {
- fixture.innerHTML =
+ fixtureSetup(
'' +
' link' +
' other link' +
- '
';
+ '');
var link = document.getElementById('link');
assert.isFalse(axe.commons.dom.isInTextBlock(link));
});
it('ignores hidden content', function () {
- fixture.innerHTML =
+ fixtureSetup(
'' +
' link' +
' some hidden text' +
- '
';
+ '');
var link = document.getElementById('link');
assert.isFalse(axe.commons.dom.isInTextBlock(link));
});
it('ignores floated content', function () {
- fixture.innerHTML =
+ fixtureSetup(
'' +
' A floating text in the area' +
' link' +
- '
';
+ '');
var link = document.getElementById('link');
assert.isFalse(axe.commons.dom.isInTextBlock(link));
});
it('ignores positioned content', function () {
- fixture.innerHTML =
+ fixtureSetup(
'' +
' Some absolute potitioned text' +
' link' +
- '
';
+ '');
var link = document.getElementById('link');
assert.isFalse(axe.commons.dom.isInTextBlock(link));
});
it('ignores none-text content', function () {
- fixture.innerHTML =
+ fixtureSetup(
'' +
' ' +
' link' +
- '
';
+ '');
var link = document.getElementById('link');
assert.isFalse(axe.commons.dom.isInTextBlock(link));
});
it('ignore text in the block coming before a br', function () {
- fixture.innerHTML =
+ fixtureSetup(
'Some paragraph with text
' +
' link' +
- '
';
+ '');
var link = document.getElementById('link');
assert.isFalse(axe.commons.dom.isInTextBlock(link));
});
it('ignore text in the block coming after a br', function () {
- fixture.innerHTML =
+ fixtureSetup(
'' +
' link
' +
' Some paragraph with text ' +
- '
';
+ '');
var link = document.getElementById('link');
assert.isFalse(axe.commons.dom.isInTextBlock(link));
});
it('ignore text in the block coming before and after a br', function () {
- fixture.innerHTML =
+ fixtureSetup(
'Some paragraph with text
' +
' link
' +
' Some paragraph with text ' +
- '
';
+ '');
var link = document.getElementById('link');
assert.isFalse(axe.commons.dom.isInTextBlock(link));
});
it('treats hr elements the same as br elements', function () {
- fixture.innerHTML =
- 'Some paragraph with text
' +
+ fixtureSetup(
+ 'Some paragraph with text
' +
'
link
' +
' Some paragraph with text ' +
- '';
+ '
');
var link = document.getElementById('link');
assert.isFalse(axe.commons.dom.isInTextBlock(link));
});
it('ignore comments', function () {
- fixture.innerHTML =
+ fixtureSetup(
'' +
' link' +
- '
';
+ '');
var link = document.getElementById('link');
assert.isFalse(axe.commons.dom.isInTextBlock(link));
});
+ (shadowSupport ? it : xit)('can reach outside a shadow tree', function () {
+ var div = document.createElement('div');
+ div.innerHTML = 'Some paragraph with text ';
+ var shadow = div.querySelector('span').attachShadow({ mode: 'open' });
+ shadow.innerHTML = 'link';
+ fixtureSetup(div);
+
+ var link = shadow.querySelector('#link');
+ assert.isTrue(axe.commons.dom.isInTextBlock(link));
+ });
+
+ (shadowSupport.v1 ? it : xit)('can reach into a shadow tree', function () {
+ var div = document.createElement('div');
+ div.innerHTML = 'link';
+ var shadow = div.attachShadow({ mode: 'open' });
+ shadow.innerHTML = 'Some paragraph with text
';
+ fixtureSetup(div);
+
+ var link = fixture.querySelector('#link');
+ assert.isTrue(axe.commons.dom.isInTextBlock(link));
+ });
+
+ (shadowSupport.v1 ? it : xit)('treats shadow DOM slots as siblings', function () {
+ var div = document.createElement('div');
+ div.innerHTML = '
';
+ var shadow = div.attachShadow({ mode: 'open' });
+ shadow.innerHTML = 'Some paragraph with text ' +
+ ' link
';
+ fixtureSetup(div);
+
+ var link = shadow.querySelector('#link');
+ assert.isFalse(axe.commons.dom.isInTextBlock(link));
+ });
+
});
\ No newline at end of file