-
Notifications
You must be signed in to change notification settings - Fork 784
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(target-size): correctly calculate bounding box (#4125)
* fix(target-size): correctly calculate bounding box * test rectHasMinimumSize * return early * comment and refactor * text * use neighbor target size * suggestions * fix bug
- Loading branch information
Showing
12 changed files
with
230 additions
and
84 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import findNearbyElms from './find-nearby-elms'; | ||
import isInTabOrder from './is-in-tab-order'; | ||
import { splitRects, hasVisualOverlap } from '../math'; | ||
import memoize from '../../core/utils/memoize'; | ||
|
||
export default memoize(getTargetRects); | ||
|
||
/** | ||
* Return all unobscured rects of a target. | ||
* @see https://www.w3.org/TR/WCAG22/#dfn-bounding-boxes | ||
* @param {VitualNode} vNode | ||
* @return {DOMRect[]} | ||
*/ | ||
function getTargetRects(vNode) { | ||
const nodeRect = vNode.boundingClientRect; | ||
const overlappingVNodes = findNearbyElms(vNode).filter(vNeighbor => { | ||
return ( | ||
hasVisualOverlap(vNode, vNeighbor) && | ||
vNeighbor.getComputedStylePropertyValue('pointer-events') !== 'none' && | ||
!isDescendantNotInTabOrder(vNode, vNeighbor) | ||
); | ||
}); | ||
|
||
if (!overlappingVNodes.length) { | ||
return [nodeRect]; | ||
} | ||
|
||
const obscuringRects = overlappingVNodes.map( | ||
({ boundingClientRect: rect }) => rect | ||
); | ||
return splitRects(nodeRect, obscuringRects); | ||
} | ||
|
||
function isDescendantNotInTabOrder(vAncestor, vNode) { | ||
return ( | ||
vAncestor.actualNode.contains(vNode.actualNode) && !isInTabOrder(vNode) | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
const roundingMargin = 0.05; | ||
|
||
export default function rectHasMinimumSize(minSize, { width, height }) { | ||
return ( | ||
width + roundingMargin >= minSize && height + roundingMargin >= minSize | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
describe('get-target-rects', () => { | ||
const getTargetRects = axe.commons.dom.getTargetRects; | ||
const { queryFixture } = axe.testUtils; | ||
|
||
it('returns the bounding rect when unobscured', () => { | ||
const vNode = queryFixture('<button id="target">x</button>'); | ||
const rects = getTargetRects(vNode); | ||
assert.deepEqual(rects, [vNode.actualNode.getBoundingClientRect()]); | ||
}); | ||
|
||
it('returns subset rect when obscured', () => { | ||
const vNode = queryFixture(` | ||
<button id="target" style="width: 30px; height: 40px; position: absolute; left: 10px; top: 5px">x</button> | ||
<div style="position: absolute; left: 30px; top: 0; width: 50px; height: 50px;"></div> | ||
`); | ||
const rects = getTargetRects(vNode); | ||
assert.deepEqual(rects, [new DOMRect(10, 5, 20, 40)]); | ||
}); | ||
|
||
it('ignores elements with "pointer-events: none"', () => { | ||
const vNode = queryFixture(` | ||
<button id="target" style="width: 30px; height: 40px; position: absolute; left: 10px; top: 5px">x</button> | ||
<div style="position: absolute; left: 30px; top: 0; width: 50px; height: 50px; pointer-events: none"></div> | ||
`); | ||
const rects = getTargetRects(vNode); | ||
assert.deepEqual(rects, [vNode.actualNode.getBoundingClientRect()]); | ||
}); | ||
|
||
it("ignores elements that don't overlap the target", () => { | ||
const vNode = queryFixture(` | ||
<button id="target" style="width: 30px; height: 40px; position: absolute; left: 10px; top: 5px">x</button> | ||
<div style="position: absolute; left: 60px; top: 0; width: 50px; height: 50px;"></div> | ||
`); | ||
const rects = getTargetRects(vNode); | ||
assert.deepEqual(rects, [vNode.actualNode.getBoundingClientRect()]); | ||
}); | ||
|
||
it('ignores non-tabbable descendants of the target', () => { | ||
const vNode = queryFixture(` | ||
<button id="target" style="width: 30px; height: 40px; position: absolute; left: 10px; top: 5px"> | ||
<div style="position: absolute; left: 5px; top: 5px; width: 50px; height: 50px;"></div> | ||
</button> | ||
`); | ||
const rects = getTargetRects(vNode); | ||
assert.deepEqual(rects, [vNode.actualNode.getBoundingClientRect()]); | ||
}); | ||
|
||
it('returns each unobscured area', () => { | ||
const vNode = queryFixture(` | ||
<button id="target" style="width: 30px; height: 40px; position: absolute; left: 10px; top: 5px">x</button> | ||
<div style="position: absolute; left: 30px; top: 0; width: 50px; height: 50px;"></div> | ||
<div style="position: absolute; left: 0px; top: 20px; width: 50px; height: 10px"></div> | ||
`); | ||
const rects = getTargetRects(vNode); | ||
assert.deepEqual(rects, [ | ||
new DOMRect(10, 5, 20, 15), | ||
new DOMRect(10, 30, 20, 15) | ||
]); | ||
}); | ||
|
||
it('returns empty if target is fully obscured', () => { | ||
const vNode = queryFixture(` | ||
<button id="target" style="width: 30px; height: 40px; position: absolute; left: 10px; top: 5px">x</button> | ||
<div style="position: absolute; left: 0; top: 0; width: 50px; height: 50px;"></div> | ||
`); | ||
const rects = getTargetRects(vNode); | ||
assert.lengthOf(rects, 0); | ||
}); | ||
|
||
it('returns subset rect of the target with tabbable descendant', () => { | ||
const vNode = queryFixture(` | ||
<button id="target" style="width: 30px; height: 40px; position: absolute; left: 10px; top: 5px"> | ||
<div tabindex="0" style="position: absolute; left: 5px; top: 5px; width: 50px; height: 50px;"></div> | ||
</button> | ||
`); | ||
const rects = getTargetRects(vNode); | ||
console.log(JSON.stringify(rects)); | ||
assert.deepEqual(rects, [ | ||
new DOMRect(10, 5, 30, 7), | ||
new DOMRect(10, 5, 7, 40) | ||
]); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
describe('rectHasMinimumSize', () => { | ||
const rectHasMinimumSize = axe.commons.math.rectHasMinimumSize; | ||
|
||
it('returns true if rect is large enough', () => { | ||
const rect = new DOMRect(10, 20, 10, 20); | ||
assert.isTrue(rectHasMinimumSize(10, rect)); | ||
}); | ||
|
||
it('returns true for rounding margin', () => { | ||
const rect = new DOMRect(10, 20, 9.95, 20); | ||
assert.isTrue(rectHasMinimumSize(10, rect)); | ||
}); | ||
|
||
it('returns false if width is too small', () => { | ||
const rect = new DOMRect(10, 20, 5, 20); | ||
assert.isFalse(rectHasMinimumSize(10, rect)); | ||
}); | ||
|
||
it('returns false if height is too small', () => { | ||
const rect = new DOMRect(10, 20, 10, 5); | ||
assert.isFalse(rectHasMinimumSize(10, rect)); | ||
}); | ||
|
||
it('returns false when below rounding margin', () => { | ||
const rect = new DOMRect(10, 20, 9.94, 20); | ||
assert.isFalse(rectHasMinimumSize(10, rect)); | ||
}); | ||
}); |
Oops, something went wrong.