Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(color-contrast): properly handle scrolling text #2619

Merged
merged 2 commits into from
Nov 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 16 additions & 11 deletions lib/commons/dom/get-text-element-stack.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,30 +24,31 @@ function getTextElementStack(node) {
return [];
}

// if the element is using text truncation we need to get the rect of
// the element rather than look at the text node rects as they will return
// the full width of the text node before truncation (which can go off the
// screen)
// @see https://github.com/dequelabs/axe-core/issues/2178
const whiteSpace = vNode.getComputedStylePropertyValue('white-space');
const overflow = vNode.getComputedStylePropertyValue('overflow');
if (whiteSpace === 'nowrap' && overflow === 'hidden') {
return [getElementStack(node)];
}

// for code blocks that use syntax highlighting, you can get a ton of client
// rects (See https://github.com/dequelabs/axe-core/issues/1985). they use
// a mixture of text nodes and other nodes (which will contain their own text
// nodes), but all we care about is checking the direct text nodes as the
// other nodes will have their own client rects checked. doing this speeds up
// color contrast significantly for large syntax highlighted code blocks
const nodeRect = vNode.boundingClientRect;
const clientRects = [];
Array.from(node.childNodes).forEach(elm => {
if (elm.nodeType === 3 && sanitize(elm.textContent) !== '') {
const range = document.createRange();
range.selectNodeContents(elm);
const rects = range.getClientRects();

// if the elements bounding rect is smaller than the text rects
// (for example, it's using text truncation or overflow) we
// need to get the rect of the element rather than look at the
// text node rects as they will return the full width of the
// text node (which can go off the screen)
// @see https://github.com/dequelabs/axe-core/issues/2178
// @see https://github.com/dequelabs/axe-core/issues/2483
if (rects[0] && rects[0].width > nodeRect.width) {
return;
}

for (let i = 0; i < rects.length; i++) {
const rect = rects[i];

Expand All @@ -61,6 +62,10 @@ function getTextElementStack(node) {
}
});

if (!clientRects.length) {
return [getElementStack(node)];
}

return clientRects.map(rect => {
return getRectStack(grid, rect);
});
Expand Down
13 changes: 13 additions & 0 deletions test/commons/dom/get-element-stack.js
Original file line number Diff line number Diff line change
Expand Up @@ -576,5 +576,18 @@ describe('dom.getElementStack', function() {
var stacks = getTextElementStack(target).map(mapToIDs);
assert.deepEqual(stacks, [['target', '1', 'fixture']]);
});

it('should handle text that is too large for the container', function() {
fixture.innerHTML =
'<pre id="1" style="width: 400px; overflow: auto;">' +
'<span id="target" style="display: flex; with: 400px;">' +
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed et sollicitudin quam. Fusce mi odio, egestas pulvinar erat eget, vehicula tempus est. Proin vitae ullamcorper velit. Donec sagittis est justo, mattis iaculis arcu facilisis id. Proin pulvinar ornare arcu a fermentum. Quisque et dignissim nulla, sit amet consectetur ipsum. Donec in libero porttitor, dapibus neque imperdiet, aliquam est. Vivamus blandit volutpat fringilla. In mi magna, mollis sit amet imperdiet eu, rutrum ut tellus. Mauris vel condimentum nibh, quis ultricies nisi. Vivamus accumsan quam mauris, id iaculis quam fringilla ac. Curabitur pulvinar dolor ac magna vehicula, non auctor ligula dignissim. Nam ac nibh porttitor, malesuada tortor varius, feugiat turpis. Mauris dapibus, tellus ut viverra porta, ipsum turpis bibendum ligula, at tempor felis ante non libero. Donec dapibus, diam sit amet posuere commodo, magna orci hendrerit ipsum, eu egestas mauris nulla ut ipsum. Sed luctus, orci in fringilla finibus, odio leo porta dolor, eu dignissim risus eros eget erat.' +
'</span>' +
'</pre>';
axe.testUtils.flatTreeSetup(fixture);
var target = fixture.querySelector('#target');
var stacks = getTextElementStack(target).map(mapToIDs);
assert.deepEqual(stacks, [['target', '1', 'fixture']]);
});
});
});