From c51fc33c77312542466d0b5167ce222df7ccac4a Mon Sep 17 00:00:00 2001 From: Steven Lambert Date: Fri, 28 Jul 2023 08:13:46 -0600 Subject: [PATCH 01/15] fix(target-size): update to match new spacing requirements --- lib/commons/dom/get-target-size.js | 130 ++++++++++++++++ lib/commons/dom/index.js | 1 + test/playground.html | 230 +++++++++++++++++++++++++++-- 3 files changed, 346 insertions(+), 15 deletions(-) create mode 100644 lib/commons/dom/get-target-size.js diff --git a/lib/commons/dom/get-target-size.js b/lib/commons/dom/get-target-size.js new file mode 100644 index 0000000000..aa41801847 --- /dev/null +++ b/lib/commons/dom/get-target-size.js @@ -0,0 +1,130 @@ +import findNearbyElms from './find-nearby-elms'; +import isInTabOrder from './is-in-tab-order'; +import isFocusable from './is-focusable'; +import { getRoleType } from '../aria'; +import { splitRects, hasVisualOverlap } from '../math'; +import memoize from '../../core/utils/memoize'; + +export default memoize(getTargetSize); + +/** + * Compute the target size of an element. + * @see https://www.w3.org/TR/WCAG22/#dfn-targets + */ +function getTargetSize(vNode) { + const nodeRect = vNode.boundingClientRect; + const overlappingVNodes = findNearbyElms(vNode) + .filter(filterTargetableWidgets) + .filter(vNeighbor => { + return hasVisualOverlap(vNode, vNeighbor) + }); + + if (!overlappingVNodes.length) { + return { + rect: nodeRect, + overlappingVNodes + }; + } + + return { + rect: getLargestUnobscuredArea(vNode, overlappingVNodes), + overlappingVNodes + }; +} + +function filterTargetableWidgets(vNode) { + return ( + getRoleType(vNode) === 'widget' && + isFocusable(vNode) && + vNode.getComputedStylePropertyValue('pointer-events') !== 'none' + ); +} + +// // Filter nearby elements based on if they are overflowing content of vNode +// // Except if the overflowing content is a descendant in the tab order +// function filterOverflowingContent(vNode, nearbyElms) { +// return nearbyElms.filter( +// nearbyElm => +// !isEnclosedRect(nearbyElm, vNode) && +// isDescendantNotInTabOrder(vNode, nearbyElm) +// ); +// } + +// function isEnclosedRect(vNodeA, vNodeB) { +// const rectA = vNodeA.boundingClientRect; +// const rectB = vNodeB.boundingClientRect; +// return ( +// rectA.top >= rectB.top && +// rectA.left >= rectB.left && +// rectA.bottom <= rectB.bottom && +// rectA.right <= rectB.right +// ); +// } + +// function isDescendantNotInTabOrder(vAncestor, vNode) { +// return ( +// vAncestor.actualNode.contains(vNode.actualNode) && !isInTabOrder(vNode) +// ); +// } + +// // Return fully and partially obscuring nodes from nearby nodes +// function filterByElmsOverlap(vNode, nearbyElms) { +// const fullyObscuringElms = []; +// const partialObscuringElms = []; +// for (const vNeighbor of nearbyElms) { +// // Determine if the element is obscured +// if ( +// !isDescendantNotInTabOrder(vNode, vNeighbor) && +// hasVisualOverlap(vNode, vNeighbor) && +// getCssPointerEvents(vNeighbor) !== 'none' +// ) { +// // Group by fully or partially obscured +// if (isEnclosedRect(vNode, vNeighbor)) { +// fullyObscuringElms.push(vNeighbor); +// } else { +// partialObscuringElms.push(vNeighbor); +// } +// } +// } +// return { fullyObscuringElms, partialObscuringElms }; +// } + +// function getCssPointerEvents(vNode) { +// return vNode.getComputedStylePropertyValue('pointer-events'); +// } + +// // Filter only focusable widgets +// function filterFocusableWidgets(vNodes) { +// return vNodes.filter( +// vNode => getRoleType(vNode) === 'widget' && isFocusable(vNode) +// ); +// } + +// Find areas of the target that are not obscured +function getLargestUnobscuredArea(vNode, obscuredNodes) { + const nodeRect = vNode.boundingClientRect; + if (obscuredNodes.length === 0) { + return null; + } + const obscuringRects = obscuredNodes.map( + ({ boundingClientRect: rect }) => rect + ); + const unobscuredRects = splitRects(nodeRect, obscuringRects); + // Of the unobscured inner rects, work out the largest + return getLargestRect(unobscuredRects); +} + +// Find the largest rectangle in the array, prioritize ones that meet a minimum size +function getLargestRect(rects, minSize) { + return rects.reduce((rectA, rectB) => { + const rectAisMinimum = rectHasMinimumSize(minSize, rectA); + const rectBisMinimum = rectHasMinimumSize(minSize, rectB); + // Prioritize rects that pass the minimum + if (rectAisMinimum !== rectBisMinimum) { + return rectAisMinimum ? rectA : rectB; + } + const areaA = rectA.width * rectA.height; + const areaB = rectB.width * rectB.height; + return areaA > areaB ? rectA : rectB; + }); +} \ No newline at end of file diff --git a/lib/commons/dom/index.js b/lib/commons/dom/index.js index 9e5f16a741..fbac8213f0 100644 --- a/lib/commons/dom/index.js +++ b/lib/commons/dom/index.js @@ -17,6 +17,7 @@ export { default as getOverflowHiddenAncestors } from './get-overflow-hidden-anc export { default as getRootNode } from './get-root-node'; export { default as getScrollOffset } from './get-scroll-offset'; export { default as getTabbableElements } from './get-tabbable-elements'; +export { default as getTargetSize } from './get-target-size'; export { default as getTextElementStack } from './get-text-element-stack'; export { default as getViewportSize } from './get-viewport-size'; export { default as getVisibleChildTextRects } from './get-visible-child-text-rects'; diff --git a/test/playground.html b/test/playground.html index 6f90484add..67c7434695 100644 --- a/test/playground.html +++ b/test/playground.html @@ -5,25 +5,225 @@

Hello World

+ + + +

+ Wide link + x +

+ +

+ + + + + +

+ + +

+ + + +

+ +

+ + Wide link + +

+ + +

+ x +

+ + + +

+ +

+ + + + +

+ x + Wide link +

+ +

+ abc + Wide link + Wide link +

+ +

+ +   + M1 + +   + M4 + +

+ +

+ + + M3 + +
+ + M2 + +

+ + +

+ The quick brown
+ fox jumped over the lazy dog. +

+ + + + Hello + + + + + + +
+ +
+ From d37cc753d40d94746592b3db9871a6991805ddf8 Mon Sep 17 00:00:00 2001 From: Steven Lambert Date: Tue, 1 Aug 2023 08:34:45 -0600 Subject: [PATCH 02/15] working --- lib/commons/dom/get-target-size.js | 75 +++---------- lib/commons/math/get-offset.js | 171 +++++++++-------------------- lib/commons/math/split-rects.js | 25 +++-- test/commons/math/get-offset.js | 111 ++----------------- test/commons/math/split-rects.js | 100 +++++++++-------- test/playground.html | 55 +++++++--- 6 files changed, 187 insertions(+), 350 deletions(-) diff --git a/lib/commons/dom/get-target-size.js b/lib/commons/dom/get-target-size.js index aa41801847..f514630cf4 100644 --- a/lib/commons/dom/get-target-size.js +++ b/lib/commons/dom/get-target-size.js @@ -5,6 +5,8 @@ import { getRoleType } from '../aria'; import { splitRects, hasVisualOverlap } from '../math'; import memoize from '../../core/utils/memoize'; +const roundingMargin = 0.05; + export default memoize(getTargetSize); /** @@ -40,66 +42,6 @@ function filterTargetableWidgets(vNode) { ); } -// // Filter nearby elements based on if they are overflowing content of vNode -// // Except if the overflowing content is a descendant in the tab order -// function filterOverflowingContent(vNode, nearbyElms) { -// return nearbyElms.filter( -// nearbyElm => -// !isEnclosedRect(nearbyElm, vNode) && -// isDescendantNotInTabOrder(vNode, nearbyElm) -// ); -// } - -// function isEnclosedRect(vNodeA, vNodeB) { -// const rectA = vNodeA.boundingClientRect; -// const rectB = vNodeB.boundingClientRect; -// return ( -// rectA.top >= rectB.top && -// rectA.left >= rectB.left && -// rectA.bottom <= rectB.bottom && -// rectA.right <= rectB.right -// ); -// } - -// function isDescendantNotInTabOrder(vAncestor, vNode) { -// return ( -// vAncestor.actualNode.contains(vNode.actualNode) && !isInTabOrder(vNode) -// ); -// } - -// // Return fully and partially obscuring nodes from nearby nodes -// function filterByElmsOverlap(vNode, nearbyElms) { -// const fullyObscuringElms = []; -// const partialObscuringElms = []; -// for (const vNeighbor of nearbyElms) { -// // Determine if the element is obscured -// if ( -// !isDescendantNotInTabOrder(vNode, vNeighbor) && -// hasVisualOverlap(vNode, vNeighbor) && -// getCssPointerEvents(vNeighbor) !== 'none' -// ) { -// // Group by fully or partially obscured -// if (isEnclosedRect(vNode, vNeighbor)) { -// fullyObscuringElms.push(vNeighbor); -// } else { -// partialObscuringElms.push(vNeighbor); -// } -// } -// } -// return { fullyObscuringElms, partialObscuringElms }; -// } - -// function getCssPointerEvents(vNode) { -// return vNode.getComputedStylePropertyValue('pointer-events'); -// } - -// // Filter only focusable widgets -// function filterFocusableWidgets(vNodes) { -// return vNodes.filter( -// vNode => getRoleType(vNode) === 'widget' && isFocusable(vNode) -// ); -// } - // Find areas of the target that are not obscured function getLargestUnobscuredArea(vNode, obscuredNodes) { const nodeRect = vNode.boundingClientRect; @@ -110,8 +52,13 @@ function getLargestUnobscuredArea(vNode, obscuredNodes) { ({ boundingClientRect: rect }) => rect ); const unobscuredRects = splitRects(nodeRect, obscuringRects); + if (!unobscuredRects.length) { + return null; + } + // Of the unobscured inner rects, work out the largest - return getLargestRect(unobscuredRects); + // TODO: pass option here + return getLargestRect(unobscuredRects, 24); } // Find the largest rectangle in the array, prioritize ones that meet a minimum size @@ -127,4 +74,10 @@ function getLargestRect(rects, minSize) { const areaB = rectB.width * rectB.height; return areaA > areaB ? rectA : rectB; }); +} + +function rectHasMinimumSize(minSize, { width, height }) { + return ( + width + roundingMargin >= minSize && height + roundingMargin >= minSize + ); } \ No newline at end of file diff --git a/lib/commons/math/get-offset.js b/lib/commons/math/get-offset.js index 408a3e7cef..049b80d834 100644 --- a/lib/commons/math/get-offset.js +++ b/lib/commons/math/get-offset.js @@ -1,3 +1,7 @@ +import { getTargetSize } from '../dom'; + +const roundingMargin = 0.05; + /** * Get the offset between node A and node B * @method getOffset @@ -6,86 +10,62 @@ * @param {VirtualNode} vNodeB * @returns {number} */ -export default function getOffset(vNodeA, vNodeB) { - const rectA = vNodeA.boundingClientRect; - const rectB = vNodeB.boundingClientRect; - const pointA = getFarthestPoint(rectA, rectB); - const pointB = getClosestPoint(pointA, rectA, rectB); - return pointDistance(pointA, pointB); +export default function getOffset(vNodeA, vNodeB, minSize = 24) { + const { rect: rectA } = getTargetSize(vNodeA); + const { rect: rectB } = getTargetSize(vNodeB); + + // one of the rects is fully obscured + if (rectA === null || rectB === null) { + return 0; + } + + const centerA = { + x: rectA.x + rectA.width / 2, + y: rectA.y + rectA.height / 2 + }; + const centerB = { + x: rectB.x + rectB.width / 2, + y: rectB.y + rectB.height / 2 + }; + const sideB = getClosestPoint(centerA, rectB); + + return Math.min( + pointDistance(centerA, centerB), + pointDistance(centerA, sideB) + ); } -/** - * Get a point on rectA that is farthest away from rectB - * @param {Rect} rectA - * @param {Rect} rectB - * @returns {Point} - */ -function getFarthestPoint(rectA, rectB) { - const dimensionProps = [ - ['x', 'left', 'right', 'width'], - ['y', 'top', 'bottom', 'height'] - ]; - const farthestPoint = {}; - dimensionProps.forEach(([axis, start, end, diameter]) => { - if (rectB[start] < rectA[start] && rectB[end] > rectA[end]) { - farthestPoint[axis] = rectA[start] + rectA[diameter] / 2; // center | middle - return; - } - // Work out which edge of A is farthest away from the center of B - const centerB = rectB[start] + rectB[diameter] / 2; - const startDistance = Math.abs(centerB - rectA[start]); - const endDistance = Math.abs(centerB - rectA[end]); - if (startDistance >= endDistance) { - farthestPoint[axis] = rectA[start]; // left | top - } else { - farthestPoint[axis] = rectA[end]; // right | bottom - } - }); - return farthestPoint; +function rectHasMinimumSize(minSize, { width, height }) { + return ( + width + roundingMargin >= minSize && height + roundingMargin >= minSize + ); } -/** - * Get a point on the adjacentRect, that is as close the point given from ownRect - * @param {Point} ownRectPoint - * @param {Rect} ownRect - * @param {Rect} adjacentRect - * @returns {Point} - */ -function getClosestPoint({ x, y }, ownRect, adjacentRect) { - if (pointInRect({ x, y }, adjacentRect)) { - // Check if there is an opposite corner inside the adjacent rectangle - const closestPoint = getCornerInAdjacentRect( - { x, y }, - ownRect, - adjacentRect - ); - if (closestPoint !== null) { - return closestPoint; - } - adjacentRect = ownRect; - } +function getClosestPoint(point, rect) { + let x; + let y; - const { top, right, bottom, left } = adjacentRect; - // Is the adjacent rect horizontally or vertically aligned - const xAligned = x >= left && x <= right; - const yAligned = y >= top && y <= bottom; - // Find the closest edge of the adjacent rect - const closestX = Math.abs(left - x) < Math.abs(right - x) ? left : right; - const closestY = Math.abs(top - y) < Math.abs(bottom - y) ? top : bottom; + if (point.x < rect.left) { + x = rect.left; + } + else if (point.x > rect.right) { + x = rect.right; + } + else { + x = point.x; + } - if (!xAligned && yAligned) { - return { x: closestX, y }; // Closest horizontal point - } else if (xAligned && !yAligned) { - return { x, y: closestY }; // Closest vertical point - } else if (!xAligned && !yAligned) { - return { x: closestX, y: closestY }; // Closest diagonal corner + if (point.y < rect.top) { + y = rect.top; + } + else if (point.y > rect.bottom) { + y = rect.bottom; } - // ownRect (partially) obscures adjacentRect - if (Math.abs(x - closestX) < Math.abs(y - closestY)) { - return { x: closestX, y }; // Inside, closest edge is horizontal - } else { - return { x, y: closestY }; // Inside, closest edge is vertical + else { + y = point.y; } + + return { x, y }; } /** @@ -101,49 +81,4 @@ function pointDistance(pointA, pointB) { return xDistance || yDistance; // If either is 0, return the other } return Math.sqrt(Math.pow(xDistance, 2) + Math.pow(yDistance, 2)); -} - -/** - * Return if a point is within a rect - * @param {Point} point - * @param {Rect} rect - * @returns {boolean} - */ -function pointInRect({ x, y }, rect) { - return y >= rect.top && x <= rect.right && y <= rect.bottom && x >= rect.left; -} - -/** - * - * @param {Point} ownRectPoint - * @param {Rect} ownRect - * @param {Rect} adjacentRect - * @returns {Point | null} With x and y - */ -function getCornerInAdjacentRect({ x, y }, ownRect, adjacentRect) { - let closestX, closestY; - // Find the opposite corner, if it is inside the adjacent rect; - if (x === ownRect.left && ownRect.right < adjacentRect.right) { - closestX = ownRect.right; - } else if (x === ownRect.right && ownRect.left > adjacentRect.left) { - closestX = ownRect.left; - } - if (y === ownRect.top && ownRect.bottom < adjacentRect.bottom) { - closestY = ownRect.bottom; - } else if (y === ownRect.bottom && ownRect.top > adjacentRect.top) { - closestY = ownRect.top; - } - - if (!closestX && !closestY) { - return null; // opposite corners are outside the rect, or {x,y} was a center point - } else if (!closestY) { - return { x: closestX, y }; - } else if (!closestX) { - return { x, y: closestY }; - } - if (Math.abs(x - closestX) < Math.abs(y - closestY)) { - return { x: closestX, y }; - } else { - return { x, y: closestY }; - } -} +} \ No newline at end of file diff --git a/lib/commons/math/split-rects.js b/lib/commons/math/split-rects.js index 0273445f3a..a9b783c2cb 100644 --- a/lib/commons/math/split-rects.js +++ b/lib/commons/math/split-rects.js @@ -5,7 +5,7 @@ * @memberof axe.commons.math * @param {DOMRect} outerRect * @param {DOMRect[]} overlapRects - * @returns {Rect[]} Unique array of rects + * @returns {DOMRect[]} Unique array of rects */ export default function splitRects(outerRect, overlapRects) { let uniqueRects = [outerRect]; @@ -37,19 +37,28 @@ function splitRect(inputRect, clipRect) { rects.push({ top, left, bottom, right: clipRect.left }); } if (rects.length === 0) { + // Fully overlapping + if (isEnclosedRect(inputRect, clipRect)) { + return []; + } + rects.push(inputRect); // No intersection } + return rects.map(computeRect); // add x / y / width / height } const between = (num, min, max) => num > min && num < max; function computeRect(baseRect) { - return { - ...baseRect, - x: baseRect.left, - y: baseRect.top, - height: baseRect.bottom - baseRect.top, - width: baseRect.right - baseRect.left - }; + return new DOMRect(baseRect.left, baseRect.top, baseRect.right - baseRect.left, baseRect.bottom - baseRect.top); } + +function isEnclosedRect(rectA, rectB) { + return ( + rectA.top >= rectB.top && + rectA.left >= rectB.left && + rectA.bottom <= rectB.bottom && + rectA.right <= rectB.right + ); +} \ No newline at end of file diff --git a/test/commons/math/get-offset.js b/test/commons/math/get-offset.js index 9214d6a861..fc6dd58234 100644 --- a/test/commons/math/get-offset.js +++ b/test/commons/math/get-offset.js @@ -1,104 +1,17 @@ -describe('getOffset', function () { +describe.only('getOffset', () => { 'use strict'; - var fixtureSetup = axe.testUtils.fixtureSetup; - var getOffset = axe.commons.math.getOffset; - var round = 0.2; - - // Return the diagonal of a square of size X, or rectangle of size X * Y - function getDiagonal(x, y) { - y = typeof y === 'number' ? y : x; - return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); - } - - it('returns with + spacing for horizontally adjacent elms', function () { - var fixture = fixtureSetup( - ' ' + - ' ' - ); - var nodeA = fixture.children[0]; - var nodeB = fixture.children[1]; - assert.closeTo(getOffset(nodeA, nodeB), 40, round); - assert.closeTo(getOffset(nodeB, nodeA), 30, round); - }); - - it('returns closest horizontal distance for elements horizontally aligned', function () { - var fixture = fixtureSetup( - ' ' + - ' ' - ); - var nodeA = fixture.children[0]; - var nodeB = fixture.children[1]; - assert.closeTo(getOffset(nodeA, nodeB), getDiagonal(40, 5), round); - assert.closeTo(getOffset(nodeB, nodeA), 30, round); - }); - - it('returns height + spacing for vertically adjacent elms', function () { - var fixture = fixtureSetup( - ' ' + - ' ' - ); - var nodeA = fixture.children[0]; - var nodeB = fixture.children[1]; - assert.closeTo(getOffset(nodeA, nodeB), 40, round); - assert.closeTo(getOffset(nodeB, nodeA), 30, round); - }); - - it('returns closest vertical distance for elements horizontally aligned', function () { - var fixture = fixtureSetup( - ' ' + - ' ' - ); - var nodeA = fixture.children[0]; - var nodeB = fixture.children[1]; - - assert.closeTo(getOffset(nodeA, nodeB), getDiagonal(40, 10), round); - assert.closeTo(getOffset(nodeB, nodeA), 30, round); - }); - - it('returns corner to corner distance for diagonal elms', function () { - var fixture = fixtureSetup( - ' ' + - ' ' - ); - var nodeA = fixture.children[0]; - var nodeB = fixture.children[1]; - assert.closeTo(getOffset(nodeA, nodeB), getDiagonal(40), round); - assert.closeTo(getOffset(nodeB, nodeA), getDiagonal(30), round); - }); - - it('returns the distance to the edge when elements overlap on an edge', function () { - var fixture = fixtureSetup( - '' + - ' ' + - '' - ); - var nodeA = fixture.children[0]; - var nodeB = nodeA.children[0]; + const fixtureSetup = axe.testUtils.fixtureSetup; + const getOffset = axe.commons.math.getOffset; + const round = 0.2; + + it('returns center to center distance when both are undersized', () => { + const fixture = fixtureSetup(` + + + `); + const nodeA = fixture.children[0]; + const nodeB = fixture.children[1]; assert.closeTo(getOffset(nodeA, nodeB), 30, round); assert.closeTo(getOffset(nodeB, nodeA), 30, round); }); - - it('returns the shortest side of the element when an element overlaps on a corner', function () { - var fixture = fixtureSetup( - '' + - ' ' + - '' - ); - var nodeA = fixture.children[0]; - var nodeB = nodeA.children[0]; - assert.closeTo(getOffset(nodeA, nodeB), getDiagonal(30), round); - assert.closeTo(getOffset(nodeB, nodeA), 20, round); - }); - - it('returns smallest diagonal if elmA fully covers elmB', function () { - var fixture = fixtureSetup( - '' + - ' ' + - '' - ); - var nodeA = fixture.children[0]; - var nodeB = nodeA.children[0]; - assert.closeTo(getOffset(nodeA, nodeB), getDiagonal(10), round); - assert.closeTo(getOffset(nodeB, nodeA), 10, round); - }); }); diff --git a/test/commons/math/split-rects.js b/test/commons/math/split-rects.js index 714e03f24d..9896171c75 100644 --- a/test/commons/math/split-rects.js +++ b/test/commons/math/split-rects.js @@ -1,92 +1,98 @@ -describe('splitRects', function () { - var splitRects = axe.commons.math.splitRects; +describe('splitRects', () => { + const splitRects = axe.commons.math.splitRects; function createRect(x, y, width, height) { - return { - x: x, - y: y, - width: width, - height: height, - top: y, - left: x, - bottom: y + height, - right: x + width - }; + return new DOMRect(x, y, width, height); } - it('returns the original rect if there is no clipping rect', function () { - var rectA = createRect(0, 0, 100, 50); - var rects = splitRects(rectA, []); + it('returns the original rect if there is no clipping rect', () => { + const rectA = createRect(0, 0, 100, 50); + const rects = splitRects(rectA, []); assert.lengthOf(rects, 1); assert.deepEqual(rects[0], rectA); }); - it('returns the original rect if there is no overlap', function () { - var rectA = createRect(0, 0, 100, 50); - var rectB = createRect(0, 50, 50, 50); - var rects = splitRects(rectA, [rectB]); + it('returns the original rect if there is no overlap', () => { + const rectA = createRect(0, 0, 100, 50); + const rectB = createRect(0, 50, 50, 50); + const rects = splitRects(rectA, [rectB]); assert.lengthOf(rects, 1); assert.deepEqual(rects[0], rectA); }); - describe('with one overlapping rect', function () { - it('returns one rect if overlaps covers two corners', function () { - var rectA = createRect(0, 0, 100, 50); - var rectB = createRect(40, 0, 100, 50); - var rects = splitRects(rectA, [rectB]); + describe('with one overlapping rect', () => { + it('returns one rect if overlaps covers two corners', () => { + const rectA = createRect(0, 0, 100, 50); + const rectB = createRect(40, 0, 100, 50); + const rects = splitRects(rectA, [rectB]); assert.lengthOf(rects, 1); assert.deepEqual(rects[0], createRect(0, 0, 40, 50)); }); - it('returns two rects if overlap covers one corner', function () { - var rectA = createRect(0, 0, 100, 100); - var rectB = createRect(50, 50, 50, 50); - var rects = splitRects(rectA, [rectB]); + it('returns two rects if overlap covers one corner', () => { + const rectA = createRect(0, 0, 100, 100); + const rectB = createRect(50, 50, 50, 50); + const rects = splitRects(rectA, [rectB]); assert.lengthOf(rects, 2); assert.deepEqual(rects[0], createRect(0, 0, 100, 50)); assert.deepEqual(rects[1], createRect(0, 0, 50, 100)); }); - it('returns three rects if overlap covers an edge, but no corner', function () { - var rectA = createRect(0, 0, 100, 150); - var rectB = createRect(50, 50, 50, 50); - var rects = splitRects(rectA, [rectB]); + it('returns three rects if overlap covers an edge, but no corner', () => { + const rectA = createRect(0, 0, 100, 150); + const rectB = createRect(50, 50, 50, 50); + const rects = splitRects(rectA, [rectB]); assert.lengthOf(rects, 3); assert.deepEqual(rects[0], createRect(0, 0, 100, 50)); assert.deepEqual(rects[1], createRect(0, 100, 100, 50)); assert.deepEqual(rects[2], createRect(0, 0, 50, 150)); }); - it('returns four rects if overlap sits in the middle, touching no corner', function () { - var rectA = createRect(0, 0, 150, 150); - var rectB = createRect(50, 50, 50, 50); - var rects = splitRects(rectA, [rectB]); + it('returns four rects if overlap sits in the middle, touching no corner', () => { + const rectA = createRect(0, 0, 150, 150); + const rectB = createRect(50, 50, 50, 50); + const rects = splitRects(rectA, [rectB]); assert.lengthOf(rects, 4); assert.deepEqual(rects[0], createRect(0, 0, 150, 50)); assert.deepEqual(rects[1], createRect(100, 0, 50, 150)); assert.deepEqual(rects[2], createRect(0, 100, 150, 50)); assert.deepEqual(rects[3], createRect(0, 0, 50, 150)); }); + + it('returns no rects if overlap covers the entire input rect', () => { + const rectA = createRect(0, 0, 100, 50); + const rectB = createRect(-50, -50, 400, 400); + const rects = splitRects(rectA, [rectB]); + assert.lengthOf(rects, 0); + }); }); - describe('with multiple overlaps', function () { - it('can return a single rect two overlaps each cover an edge', function () { - var rectA = createRect(0, 0, 150, 50); - var rectB = createRect(0, 0, 50, 50); - var rectC = createRect(100, 0, 50, 50); - var rects = splitRects(rectA, [rectB, rectC]); + describe('with multiple overlaps', () => { + it('can return a single rect two overlaps each cover an edge', () => { + const rectA = createRect(0, 0, 150, 50); + const rectB = createRect(0, 0, 50, 50); + const rectC = createRect(100, 0, 50, 50); + const rects = splitRects(rectA, [rectB, rectC]); assert.lengthOf(rects, 1); assert.deepEqual(rects[0], createRect(50, 0, 50, 50)); }); - it('can recursively clips regions', function () { - var rectA = createRect(0, 0, 150, 100); - var rectB = createRect(0, 50, 50, 50); - var rectC = createRect(100, 50, 50, 50); - var rects = splitRects(rectA, [rectB, rectC]); + it('can recursively clips regions', () => { + const rectA = createRect(0, 0, 150, 100); + const rectB = createRect(0, 50, 50, 50); + const rectC = createRect(100, 50, 50, 50); + const rects = splitRects(rectA, [rectB, rectC]); assert.lengthOf(rects, 3); assert.deepEqual(rects[0], createRect(0, 0, 150, 50)); assert.deepEqual(rects[1], createRect(50, 0, 100, 50)); assert.deepEqual(rects[2], createRect(50, 0, 50, 100)); }); + + it('returns no rects if overlap covers the entire input rect', () => { + const rectA = createRect(0, 0, 100, 50); + const rectB = createRect(50, 50, 200, 200); + const rectC = createRect(-50, -50, 200, 200); + const rects = splitRects(rectA, [rectB, rectC]); + assert.lengthOf(rects, 0); + }); }); }); diff --git a/test/playground.html b/test/playground.html index 67c7434695..f8a44ad23b 100644 --- a/test/playground.html +++ b/test/playground.html @@ -187,6 +187,16 @@

Hello World

- - -

- Wide link - x -

- -

- - - - - -

- - -

- - - -

- -

- - Wide link - -

- - -

- x -

- - - -

- -

- - - - -

- x - Wide link -

- -

- abc - Wide link - Wide link -

- -

- -   - M1 - -   - M4 - -

- -

- - - M3 - -
- - M2 - -

- - -

- The quick brown
- fox jumped over the lazy dog. -

- - - - Hello - - - - - - - - -
- From 122183381e542025fef148b948b2e6b94ca17eb8 Mon Sep 17 00:00:00 2001 From: straker Date: Wed, 2 Aug 2023 20:58:02 +0000 Subject: [PATCH 07/15] :robot: Automated formatting fixes --- lib/commons/math/get-offset.js | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/lib/commons/math/get-offset.js b/lib/commons/math/get-offset.js index 1e195a98e9..0fa0ebe4de 100644 --- a/lib/commons/math/get-offset.js +++ b/lib/commons/math/get-offset.js @@ -41,21 +41,17 @@ function getClosestPoint(point, rect) { if (point.x < rect.left) { x = rect.left; - } - else if (point.x > rect.right) { + } else if (point.x > rect.right) { x = rect.right; - } - else { + } else { x = point.x; } if (point.y < rect.top) { y = rect.top; - } - else if (point.y > rect.bottom) { + } else if (point.y > rect.bottom) { y = rect.bottom; - } - else { + } else { y = point.y; } @@ -70,4 +66,4 @@ function getClosestPoint(point, rect) { */ function pointDistance(pointA, pointB) { return Math.hypot(pointA.x - pointB.x, pointA.y - pointB.y); -} \ No newline at end of file +} From b37949804f7572ba97ec96f168a8f3f3f71c3b77 Mon Sep 17 00:00:00 2001 From: Steven Lambert <2433219+straker@users.noreply.github.com> Date: Fri, 4 Aug 2023 08:08:01 -0600 Subject: [PATCH 08/15] Apply suggestions from code review Co-authored-by: Wilco Fiers --- lib/commons/math/get-offset.js | 2 +- test/checks/mobile/target-offset.js | 1 - test/commons/math/get-offset.js | 5 ++--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/commons/math/get-offset.js b/lib/commons/math/get-offset.js index 0fa0ebe4de..ee3890e90d 100644 --- a/lib/commons/math/get-offset.js +++ b/lib/commons/math/get-offset.js @@ -9,7 +9,7 @@ import { getTargetSize } from '../dom'; * @param {Number} radius * @returns {number} */ -export default function getOffset(vNodeA, vNodeB, radius = 12) { +export default function getOffset(vNodeA, vNodeB, minRadiusNeighbour = 12) { const { rect: rectA } = getTargetSize(vNodeA); const { rect: rectB } = getTargetSize(vNodeB); diff --git a/test/checks/mobile/target-offset.js b/test/checks/mobile/target-offset.js index b8d610a5a2..d0949ebedd 100644 --- a/test/checks/mobile/target-offset.js +++ b/test/checks/mobile/target-offset.js @@ -1,5 +1,4 @@ describe('target-offset tests', () => { - 'use strict'; const checkContext = axe.testUtils.MockCheckContext(); const { checkSetup, getCheckEvaluate } = axe.testUtils; diff --git a/test/commons/math/get-offset.js b/test/commons/math/get-offset.js index 3bfa0f575a..24c1f1c548 100644 --- a/test/commons/math/get-offset.js +++ b/test/commons/math/get-offset.js @@ -1,5 +1,4 @@ describe('getOffset', () => { - 'use strict'; const fixtureSetup = axe.testUtils.fixtureSetup; const getOffset = axe.commons.math.getOffset; const round = 0.2; @@ -24,7 +23,7 @@ describe('getOffset', () => { assert.closeTo(getOffset(nodeA, nodeB), 45, round); }); - it('returns center to corner or square when at a diagonal', () => { + it('returns center to corner of square when at a diagonal', () => { const fixture = fixtureSetup(` @@ -54,7 +53,7 @@ describe('getOffset', () => { assert.equal(getOffset(nodeA, nodeB), 0); }); - it('allows passing radius', () => { + it('subtracts minNeighbourRadius from center-to-center calculations', () => { const fixture = fixtureSetup(` From 2101fbe244854053b8ab358fd94e2ab19611a91b Mon Sep 17 00:00:00 2001 From: straker Date: Fri, 4 Aug 2023 14:09:44 +0000 Subject: [PATCH 09/15] :robot: Automated formatting fixes --- test/checks/mobile/target-offset.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/checks/mobile/target-offset.js b/test/checks/mobile/target-offset.js index d0949ebedd..5f5955b48d 100644 --- a/test/checks/mobile/target-offset.js +++ b/test/checks/mobile/target-offset.js @@ -1,5 +1,4 @@ describe('target-offset tests', () => { - const checkContext = axe.testUtils.MockCheckContext(); const { checkSetup, getCheckEvaluate } = axe.testUtils; const checkEvaluate = getCheckEvaluate('target-offset'); From 7ace8bbcbdbb4b093e85d0d1266fec2548154a81 Mon Sep 17 00:00:00 2001 From: Steven Lambert Date: Fri, 4 Aug 2023 08:13:56 -0600 Subject: [PATCH 10/15] udpate tests --- test/commons/math/get-offset.js | 26 +++++------ test/commons/math/split-rects.js | 75 +++++++++++++++----------------- 2 files changed, 49 insertions(+), 52 deletions(-) diff --git a/test/commons/math/get-offset.js b/test/commons/math/get-offset.js index 24c1f1c548..cdbd1a5682 100644 --- a/test/commons/math/get-offset.js +++ b/test/commons/math/get-offset.js @@ -5,8 +5,8 @@ describe('getOffset', () => { it('returns center to edge of circle when both are undersized', () => { const fixture = fixtureSetup(` - - + + `); const nodeA = fixture.children[1]; const nodeB = fixture.children[3]; @@ -15,8 +15,8 @@ describe('getOffset', () => { it('returns center to edge of square when one is undersized', () => { const fixture = fixtureSetup(` - - + + `); const nodeA = fixture.children[1]; const nodeB = fixture.children[3]; @@ -25,18 +25,18 @@ describe('getOffset', () => { it('returns center to corner of square when at a diagonal', () => { const fixture = fixtureSetup(` - - + + `); const nodeA = fixture.children[1]; const nodeB = fixture.children[3]; - assert.closeTo(getOffset(nodeA, nodeB), 61.5, round); + assert.closeTo(getOffset(nodeA, nodeB), 63.6, round); }); it('returns 0 if nodeA is overlapped by nodeB', () => { const fixture = fixtureSetup(` - - + + `); const nodeA = fixture.children[1]; const nodeB = fixture.children[3]; @@ -45,8 +45,8 @@ describe('getOffset', () => { it('returns 0 if nodeB is overlapped by nodeA', () => { const fixture = fixtureSetup(` - - + + `); const nodeA = fixture.children[3]; const nodeB = fixture.children[1]; @@ -55,8 +55,8 @@ describe('getOffset', () => { it('subtracts minNeighbourRadius from center-to-center calculations', () => { const fixture = fixtureSetup(` - - + + `); const nodeA = fixture.children[1]; const nodeB = fixture.children[3]; diff --git a/test/commons/math/split-rects.js b/test/commons/math/split-rects.js index 9896171c75..0680b44822 100644 --- a/test/commons/math/split-rects.js +++ b/test/commons/math/split-rects.js @@ -1,19 +1,16 @@ describe('splitRects', () => { const splitRects = axe.commons.math.splitRects; - function createRect(x, y, width, height) { - return new DOMRect(x, y, width, height); - } it('returns the original rect if there is no clipping rect', () => { - const rectA = createRect(0, 0, 100, 50); + const rectA = new DOMRect(0, 0, 100, 50); const rects = splitRects(rectA, []); assert.lengthOf(rects, 1); assert.deepEqual(rects[0], rectA); }); it('returns the original rect if there is no overlap', () => { - const rectA = createRect(0, 0, 100, 50); - const rectB = createRect(0, 50, 50, 50); + const rectA = new DOMRect(0, 0, 100, 50); + const rectB = new DOMRect(0, 50, 50, 50); const rects = splitRects(rectA, [rectB]); assert.lengthOf(rects, 1); assert.deepEqual(rects[0], rectA); @@ -21,46 +18,46 @@ describe('splitRects', () => { describe('with one overlapping rect', () => { it('returns one rect if overlaps covers two corners', () => { - const rectA = createRect(0, 0, 100, 50); - const rectB = createRect(40, 0, 100, 50); + const rectA = new DOMRect(0, 0, 100, 50); + const rectB = new DOMRect(40, 0, 100, 50); const rects = splitRects(rectA, [rectB]); assert.lengthOf(rects, 1); - assert.deepEqual(rects[0], createRect(0, 0, 40, 50)); + assert.deepEqual(rects[0], new DOMRect(0, 0, 40, 50)); }); it('returns two rects if overlap covers one corner', () => { - const rectA = createRect(0, 0, 100, 100); - const rectB = createRect(50, 50, 50, 50); + const rectA = new DOMRect(0, 0, 100, 100); + const rectB = new DOMRect(50, 50, 50, 50); const rects = splitRects(rectA, [rectB]); assert.lengthOf(rects, 2); - assert.deepEqual(rects[0], createRect(0, 0, 100, 50)); - assert.deepEqual(rects[1], createRect(0, 0, 50, 100)); + assert.deepEqual(rects[0], new DOMRect(0, 0, 100, 50)); + assert.deepEqual(rects[1], new DOMRect(0, 0, 50, 100)); }); it('returns three rects if overlap covers an edge, but no corner', () => { - const rectA = createRect(0, 0, 100, 150); - const rectB = createRect(50, 50, 50, 50); + const rectA = new DOMRect(0, 0, 100, 150); + const rectB = new DOMRect(50, 50, 50, 50); const rects = splitRects(rectA, [rectB]); assert.lengthOf(rects, 3); - assert.deepEqual(rects[0], createRect(0, 0, 100, 50)); - assert.deepEqual(rects[1], createRect(0, 100, 100, 50)); - assert.deepEqual(rects[2], createRect(0, 0, 50, 150)); + assert.deepEqual(rects[0], new DOMRect(0, 0, 100, 50)); + assert.deepEqual(rects[1], new DOMRect(0, 100, 100, 50)); + assert.deepEqual(rects[2], new DOMRect(0, 0, 50, 150)); }); it('returns four rects if overlap sits in the middle, touching no corner', () => { - const rectA = createRect(0, 0, 150, 150); - const rectB = createRect(50, 50, 50, 50); + const rectA = new DOMRect(0, 0, 150, 150); + const rectB = new DOMRect(50, 50, 50, 50); const rects = splitRects(rectA, [rectB]); assert.lengthOf(rects, 4); - assert.deepEqual(rects[0], createRect(0, 0, 150, 50)); - assert.deepEqual(rects[1], createRect(100, 0, 50, 150)); - assert.deepEqual(rects[2], createRect(0, 100, 150, 50)); - assert.deepEqual(rects[3], createRect(0, 0, 50, 150)); + assert.deepEqual(rects[0], new DOMRect(0, 0, 150, 50)); + assert.deepEqual(rects[1], new DOMRect(100, 0, 50, 150)); + assert.deepEqual(rects[2], new DOMRect(0, 100, 150, 50)); + assert.deepEqual(rects[3], new DOMRect(0, 0, 50, 150)); }); it('returns no rects if overlap covers the entire input rect', () => { - const rectA = createRect(0, 0, 100, 50); - const rectB = createRect(-50, -50, 400, 400); + const rectA = new DOMRect(0, 0, 100, 50); + const rectB = new DOMRect(-50, -50, 400, 400); const rects = splitRects(rectA, [rectB]); assert.lengthOf(rects, 0); }); @@ -68,29 +65,29 @@ describe('splitRects', () => { describe('with multiple overlaps', () => { it('can return a single rect two overlaps each cover an edge', () => { - const rectA = createRect(0, 0, 150, 50); - const rectB = createRect(0, 0, 50, 50); - const rectC = createRect(100, 0, 50, 50); + const rectA = new DOMRect(0, 0, 150, 50); + const rectB = new DOMRect(0, 0, 50, 50); + const rectC = new DOMRect(100, 0, 50, 50); const rects = splitRects(rectA, [rectB, rectC]); assert.lengthOf(rects, 1); - assert.deepEqual(rects[0], createRect(50, 0, 50, 50)); + assert.deepEqual(rects[0], new DOMRect(50, 0, 50, 50)); }); it('can recursively clips regions', () => { - const rectA = createRect(0, 0, 150, 100); - const rectB = createRect(0, 50, 50, 50); - const rectC = createRect(100, 50, 50, 50); + const rectA = new DOMRect(0, 0, 150, 100); + const rectB = new DOMRect(0, 50, 50, 50); + const rectC = new DOMRect(100, 50, 50, 50); const rects = splitRects(rectA, [rectB, rectC]); assert.lengthOf(rects, 3); - assert.deepEqual(rects[0], createRect(0, 0, 150, 50)); - assert.deepEqual(rects[1], createRect(50, 0, 100, 50)); - assert.deepEqual(rects[2], createRect(50, 0, 50, 100)); + assert.deepEqual(rects[0], new DOMRect(0, 0, 150, 50)); + assert.deepEqual(rects[1], new DOMRect(50, 0, 100, 50)); + assert.deepEqual(rects[2], new DOMRect(50, 0, 50, 100)); }); it('returns no rects if overlap covers the entire input rect', () => { - const rectA = createRect(0, 0, 100, 50); - const rectB = createRect(50, 50, 200, 200); - const rectC = createRect(-50, -50, 200, 200); + const rectA = new DOMRect(0, 0, 100, 50); + const rectB = new DOMRect(50, 50, 200, 200); + const rectC = new DOMRect(-50, -50, 200, 200); const rects = splitRects(rectA, [rectB, rectC]); assert.lengthOf(rects, 0); }); From 49c303887185a527c96597e5dc7c2c4eaaefeb79 Mon Sep 17 00:00:00 2001 From: Steven Lambert Date: Fri, 4 Aug 2023 08:31:51 -0600 Subject: [PATCH 11/15] dont half minOffset but double return from getOffset --- lib/checks/mobile/target-offset-evaluate.js | 6 ++-- lib/checks/mobile/target-offset.json | 8 ++--- lib/commons/math/get-offset.js | 2 +- locales/_template.json | 8 ++--- test/checks/mobile/target-offset.js | 38 ++++++++++----------- 5 files changed, 32 insertions(+), 30 deletions(-) diff --git a/lib/checks/mobile/target-offset-evaluate.js b/lib/checks/mobile/target-offset-evaluate.js index f332c59e3b..e799cb68f1 100644 --- a/lib/checks/mobile/target-offset-evaluate.js +++ b/lib/checks/mobile/target-offset-evaluate.js @@ -5,14 +5,16 @@ import { getOffset } from '../../commons/math'; const roundingMargin = 0.05; export default function targetOffsetEvaluate(node, options, vNode) { - const minOffset = (options?.minOffset || 24) / 2; + const minOffset = options?.minOffset || 24; const closeNeighbors = []; let closestOffset = minOffset; for (const vNeighbor of findNearbyElms(vNode, minOffset)) { if (getRoleType(vNeighbor) !== 'widget' || !isFocusable(vNeighbor)) { continue; } - const offset = roundToSingleDecimal(getOffset(vNode, vNeighbor, minOffset)); + // the offset code works off radius but we want our messaging to reflect diameter + const offset = + roundToSingleDecimal(getOffset(vNode, vNeighbor, minOffset / 2)) * 2; if (offset + roundingMargin >= minOffset) { continue; } diff --git a/lib/checks/mobile/target-offset.json b/lib/checks/mobile/target-offset.json index 1954d1e970..45513ac9a8 100644 --- a/lib/checks/mobile/target-offset.json +++ b/lib/checks/mobile/target-offset.json @@ -7,11 +7,11 @@ "metadata": { "impact": "serious", "messages": { - "pass": "Target has sufficient offset from its closest neighbor (${data.closestOffset}px should be at least ${data.minOffset}px)", - "fail": "Target has insufficient offset from its closest neighbor (${data.closestOffset}px should be at least ${data.minOffset}px)", + "pass": "Target has sufficient space from its closest neighbors (${data.closestOffset}px should be at least ${data.minOffset}px)", + "fail": "Target has insufficient space to its closest neighbors. Safe clickable space has a diameter of {$data.closestOffset}px instead of at least ${data.minOffset}px)", "incomplete": { - "default": "Element with negative tabindex has insufficient offset from its closest neighbor (${data.closestOffset}px should be at least ${data.minOffset}px). Is this a target?", - "nonTabbableNeighbor": "Target has insufficient offset from a neighbor with negative tabindex (${data.closestOffset}px should be at least ${data.minOffset}px). Is the neighbor a target?" + "default": "Element with negative tabindex has insufficient space to its closest neighbors. Safe clickable space has a diameter of {$data.closestOffset}px instead of at least ${data.minOffset}px). Is this a target?", + "nonTabbableNeighbor": "Target has insufficient space to its closest neighbors. Safe clickable space has a diameter of {$data.closestOffset}px instead of at least ${data.minOffset}px). Is the neighbor a target?" } } } diff --git a/lib/commons/math/get-offset.js b/lib/commons/math/get-offset.js index ee3890e90d..61bdbbfa98 100644 --- a/lib/commons/math/get-offset.js +++ b/lib/commons/math/get-offset.js @@ -30,7 +30,7 @@ export default function getOffset(vNodeA, vNodeB, minRadiusNeighbour = 12) { return Math.min( // subtract the radius of the circle from the distance - pointDistance(centerA, centerB) - radius, + pointDistance(centerA, centerB) - minRadiusNeighbour, pointDistance(centerA, sideB) ); } diff --git a/locales/_template.json b/locales/_template.json index 44cc68bef4..06f198aa04 100644 --- a/locales/_template.json +++ b/locales/_template.json @@ -862,11 +862,11 @@ "fail": "${data} on tag disables zooming on mobile devices" }, "target-offset": { - "pass": "Target has sufficient offset from its closest neighbor (${data.closestOffset}px should be at least ${data.minOffset}px)", - "fail": "Target has insufficient offset from its closest neighbor (${data.closestOffset}px should be at least ${data.minOffset}px)", + "pass": "Target has sufficient space from its closest neighbors (${data.closestOffset}px should be at least ${data.minOffset}px)", + "fail": "Target has insufficient space to its closest neighbors. Safe clickable space has a diameter of {$data.closestOffset}px instead of at least ${data.minOffset}px)", "incomplete": { - "default": "Element with negative tabindex has insufficient offset from its closest neighbor (${data.closestOffset}px should be at least ${data.minOffset}px). Is this a target?", - "nonTabbableNeighbor": "Target has insufficient offset from a neighbor with negative tabindex (${data.closestOffset}px should be at least ${data.minOffset}px). Is the neighbor a target?" + "default": "Element with negative tabindex has insufficient space to its closest neighbors. Safe clickable space has a diameter of {$data.closestOffset}px instead of at least ${data.minOffset}px). Is this a target?", + "nonTabbableNeighbor": "Target has insufficient space to its closest neighbors. Safe clickable space has a diameter of {$data.closestOffset}px instead of at least ${data.minOffset}px). Is the neighbor a target?" } }, "target-size": { diff --git a/test/checks/mobile/target-offset.js b/test/checks/mobile/target-offset.js index 5f5955b48d..f160baa10f 100644 --- a/test/checks/mobile/target-offset.js +++ b/test/checks/mobile/target-offset.js @@ -15,11 +15,11 @@ describe('target-offset tests', () => { ); assert.isTrue(checkEvaluate.apply(checkContext, checkArgs)); - assert.equal(checkContext._data.minOffset, 12); - assert.closeTo(checkContext._data.closestOffset, 12, 0.2); + assert.equal(checkContext._data.minOffset, 24); + assert.closeTo(checkContext._data.closestOffset, 24, 0.2); }); - it('returns true when the offset is 12px', () => { + it('returns true when the offset is 24px', () => { const checkArgs = checkSetup( 'x' ); assert.isFalse(checkEvaluate.apply(checkContext, checkArgs)); - assert.equal(checkContext._data.minOffset, 12); - assert.closeTo(checkContext._data.closestOffset, 4, 0.2); + assert.equal(checkContext._data.minOffset, 24); + assert.closeTo(checkContext._data.closestOffset, 8, 0.2); const relatedIds = checkContext._relatedNodes.map(function (node) { return '#' + node.id; @@ -135,8 +135,8 @@ describe('target-offset tests', () => { ); assert.isUndefined(checkEvaluate.apply(checkContext, checkArgs)); assert.equal(checkContext._data.messageKey, 'nonTabbableNeighbor'); - assert.equal(checkContext._data.minOffset, 12); - assert.closeTo(checkContext._data.closestOffset, 4, 0.2); + assert.equal(checkContext._data.minOffset, 24); + assert.closeTo(checkContext._data.closestOffset, 8, 0.2); const relatedIds = checkContext._relatedNodes.map(function (node) { return '#' + node.id; @@ -158,8 +158,8 @@ describe('target-offset tests', () => { ); assert.isFalse(checkEvaluate.apply(checkContext, checkArgs)); assert.isUndefined(checkContext._data.messageKey); - assert.equal(checkContext._data.minOffset, 12); - assert.closeTo(checkContext._data.closestOffset, 4, 0.2); + assert.equal(checkContext._data.minOffset, 24); + assert.closeTo(checkContext._data.closestOffset, 8, 0.2); const relatedIds = checkContext._relatedNodes.map(function (node) { return '#' + node.id; From faf164519ffeb52d11ea8d82dfd3b73080ebaf11 Mon Sep 17 00:00:00 2001 From: Steven Lambert <2433219+straker@users.noreply.github.com> Date: Fri, 4 Aug 2023 08:35:44 -0600 Subject: [PATCH 12/15] Apply suggestions from code review Co-authored-by: Wilco Fiers --- lib/commons/dom/get-target-size.js | 8 ++------ lib/commons/math/get-offset.js | 4 ++-- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/lib/commons/dom/get-target-size.js b/lib/commons/dom/get-target-size.js index e395639d55..0c02501829 100644 --- a/lib/commons/dom/get-target-size.js +++ b/lib/commons/dom/get-target-size.js @@ -20,14 +20,10 @@ function getTargetSize(vNode, minSize) { }); if (!overlappingVNodes.length) { - return { - rect: nodeRect - }; + return nodeRect; } - return { - rect: getLargestUnobscuredArea(vNode, overlappingVNodes, minSize) - }; + return getLargestUnobscuredArea(vNode, overlappingVNodes, minSize) } // Find areas of the target that are not obscured diff --git a/lib/commons/math/get-offset.js b/lib/commons/math/get-offset.js index 61bdbbfa98..65cba9b0e3 100644 --- a/lib/commons/math/get-offset.js +++ b/lib/commons/math/get-offset.js @@ -10,8 +10,8 @@ import { getTargetSize } from '../dom'; * @returns {number} */ export default function getOffset(vNodeA, vNodeB, minRadiusNeighbour = 12) { - const { rect: rectA } = getTargetSize(vNodeA); - const { rect: rectB } = getTargetSize(vNodeB); + const rectA = getTargetSize(vNodeA); + const rectB = getTargetSize(vNodeB); // one of the rects is fully obscured if (rectA === null || rectB === null) { From 20373205998f181ffebb3c40ef8c952ffcf18934 Mon Sep 17 00:00:00 2001 From: straker Date: Fri, 4 Aug 2023 14:37:01 +0000 Subject: [PATCH 13/15] :robot: Automated formatting fixes --- lib/commons/dom/get-target-size.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/commons/dom/get-target-size.js b/lib/commons/dom/get-target-size.js index 0c02501829..f16c245227 100644 --- a/lib/commons/dom/get-target-size.js +++ b/lib/commons/dom/get-target-size.js @@ -23,7 +23,7 @@ function getTargetSize(vNode, minSize) { return nodeRect; } - return getLargestUnobscuredArea(vNode, overlappingVNodes, minSize) + return getLargestUnobscuredArea(vNode, overlappingVNodes, minSize); } // Find areas of the target that are not obscured From 582f5c97f5ac8fc9eb5529e78eff4511cd2ab2ba Mon Sep 17 00:00:00 2001 From: Steven Lambert Date: Mon, 7 Aug 2023 13:18:53 -0600 Subject: [PATCH 14/15] fix tests --- .../full/target-size/target-size.html | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/test/integration/full/target-size/target-size.html b/test/integration/full/target-size/target-size.html index 02a36e9a25..d6a1b2f873 100644 --- a/test/integration/full/target-size/target-size.html +++ b/test/integration/full/target-size/target-size.html @@ -350,7 +350,7 @@ -

Example E1 and E2 pass, the two outside elements of E3 and E4 fail.

+

Example E1 - E3 pass, E4 fails.

@@ -363,18 +363,18 @@
- + - +
- +
-

Example F1 and F2 pass, the inside element of F3 and F4 fail.

+

Example F1 - F3 pass, F4 fails.

@@ -388,13 +388,13 @@
- +
- + - +
@@ -440,7 +440,7 @@
- + @@ -461,7 +461,8 @@
- + +
@@ -608,7 +609,7 @@
- + From 1bdd71ea1e8634ff88732ccf8ce86030ad4d1faa Mon Sep 17 00:00:00 2001 From: Steven Lambert Date: Mon, 7 Aug 2023 13:46:55 -0600 Subject: [PATCH 15/15] fix test --- test/commons/dom/get-target-size.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/commons/dom/get-target-size.js b/test/commons/dom/get-target-size.js index dcada1b976..ddb876f7aa 100644 --- a/test/commons/dom/get-target-size.js +++ b/test/commons/dom/get-target-size.js @@ -4,7 +4,7 @@ describe('get-target-size', () => { it('returns the bounding rect when unobscured', () => { const vNode = queryFixture(''); - const { rect } = getTargetSize(vNode); + const rect = getTargetSize(vNode); assert.deepEqual(rect, vNode.actualNode.getBoundingClientRect()); }); @@ -13,7 +13,7 @@ describe('get-target-size', () => {
`); - const { rect } = getTargetSize(vNode); + const rect = getTargetSize(vNode); assert.deepEqual(rect, new DOMRect(10, 5, 20, 40)); }); @@ -22,7 +22,7 @@ describe('get-target-size', () => {
`); - const { rect } = getTargetSize(vNode); + const rect = getTargetSize(vNode); assert.deepEqual(rect, new DOMRect(10, 5, 30, 40)); }); @@ -31,7 +31,7 @@ describe('get-target-size', () => {
`); - const { rect } = getTargetSize(vNode); + const rect = getTargetSize(vNode); assert.deepEqual(rect, new DOMRect(10, 5, 30, 40)); }); @@ -41,7 +41,7 @@ describe('get-target-size', () => {
`); - const { rect } = getTargetSize(vNode); + const rect = getTargetSize(vNode); assert.deepEqual(rect, new DOMRect(10, 10, 20, 35)); }); });