Skip to content

Commit

Permalink
fix(color-contrast): handle text that is outside overflow: hidden a…
Browse files Browse the repository at this point in the history
…ncestor (#4357)

Closes: #4253

---------

Co-authored-by: Wilco Fiers <WilcoFiers@users.noreply.github.com>
  • Loading branch information
straker and WilcoFiers committed Mar 12, 2024
1 parent 54baa8d commit bdb7300
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 8 deletions.
8 changes: 7 additions & 1 deletion lib/commons/dom/get-visible-child-text-rects.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,14 @@ const getVisibleChildTextRects = memoize(
* @see https://github.com/dequelabs/axe-core/issues/2178
* @see https://github.com/dequelabs/axe-core/issues/2483
* @see https://github.com/dequelabs/axe-core/issues/2681
*
* also need to resize the nodeRect to fit within the bounds of any overflow: hidden ancestors.
*
* @see https://github.com/dequelabs/axe-core/issues/4253
*/
return clientRects.length ? clientRects : [nodeRect];
return clientRects.length
? clientRects
: filterHiddenRects([nodeRect], overflowHiddenNodes);
}
);
export default getVisibleChildTextRects;
Expand Down
24 changes: 17 additions & 7 deletions lib/rules/color-contrast-matches.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@ import {
findUpVirtual,
visuallyOverlaps,
getRootNode,
isInert
isInert,
getOverflowHiddenAncestors
} from '../commons/dom';
import {
visibleVirtual,
removeUnicode,
sanitize,
isIconLigature
} from '../commons/text';
import { rectsOverlap } from '../commons/math';
import { isDisabled } from '../commons/forms';
import { getNodeFromTree, querySelectorAll, tokenList } from '../core/utils';

Expand Down Expand Up @@ -147,14 +149,22 @@ function colorContrastMatches(node, virtualNode) {
}
}

const rects = range.getClientRects();
for (let index = 0; index < rects.length; index++) {
const rects = Array.from(range.getClientRects());
const clippingAncestors = getOverflowHiddenAncestors(virtualNode);
return rects.some(rect => {
//check to see if the rectangle impinges
if (visuallyOverlaps(rects[index], node)) {
return true;
const overlaps = visuallyOverlaps(rect, node);

if (!clippingAncestors.length) {
return overlaps;
}
}
return false;

const withinOverflow = clippingAncestors.some(overflowNode => {
return rectsOverlap(rect, overflowNode.boundingClientRect);
});

return overlaps && withinOverflow;
});
}

export default colorContrastMatches;
Expand Down
16 changes: 16 additions & 0 deletions test/commons/dom/get-visible-child-text-rects.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,20 @@ describe('dom.getVisibleChildTextRects', () => {

assert.lengthOf(actual, 2);
});

it('changes nodeRect size if all text rects got outside ancestor overflow', () => {
fixtureSetup(`
<div style="overflow: hidden; width: 50px;">
<div style="overflow: hidden; width: 25px">
<div id="target" style="padding-left: 65px;">Hello World</div>
</div>
</div>
`);
const node = fixture.querySelector('#target');
const actual = getVisibleChildTextRects(node);
const rect = getClientRects(node)[0];
const expected = new DOMRect(rect.left, rect.top, 25, rect.height);

assertRectsEqual(actual, [expected]);
});
});
6 changes: 6 additions & 0 deletions test/integration/rules/color-contrast/color-contrast.html
Original file line number Diff line number Diff line change
Expand Up @@ -459,3 +459,9 @@
>
Hello world
</div>

<div style="overflow: hidden; width: 50px">
<div style="overflow: hidden; width: 25px">
<div id="ignore13" style="padding-left: 65px">Hello World</div>
</div>
</div>
13 changes: 13 additions & 0 deletions test/rule-matches/color-contrast-matches.js
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,19 @@ describe('color-contrast-matches', function () {
assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));
});

it('should not match text outside overflow', () => {
fixture.innerHTML = `
<div style="overflow: hidden; width: 50px;">
<div style="overflow: hidden; width: 25px">
<div id="target" style="padding-left: 65px;">Hello World</div>
</div>
</div>
`;
var target = fixture.querySelector('#target');
axe.testUtils.flatTreeSetup(fixture);
assert.isFalse(rule.matches(target, axe.utils.getNodeFromTree(target)));
});

if (shadowSupport) {
it('should match a descendant of an element across a shadow boundary', function () {
fixture.innerHTML =
Expand Down

0 comments on commit bdb7300

Please sign in to comment.