Skip to content

Commit

Permalink
Merge pull request #3780 from dequelabs/release-4.5.2
Browse files Browse the repository at this point in the history
chore(release): 4.5.2
  • Loading branch information
straker authored Nov 14, 2022
2 parents 6200ce3 + fffce7d commit 8212b9e
Show file tree
Hide file tree
Showing 36 changed files with 1,789 additions and 1,582 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.

### [4.5.2](https://github.com/dequelabs/axe-core/compare/v4.5.1...v4.5.2) (2022-11-14)

### Bug Fixes

- **aria-required-children:** allow menu and menubar to be empty ([#3770](https://github.com/dequelabs/axe-core/issues/3770)) ([d11aed8](https://github.com/dequelabs/axe-core/commit/d11aed8a04968674ff872cf832cea9252023490e))
- **create-grid:** include elements scrolled out of view in the grid ([#3773](https://github.com/dequelabs/axe-core/issues/3773)) ([a563263](https://github.com/dequelabs/axe-core/commit/a5632631c72f52a5cf38a955052f28b1a931f07c))
- do not warn when using webpack ([#3777](https://github.com/dequelabs/axe-core/issues/3777)) ([d6cef9a](https://github.com/dequelabs/axe-core/commit/d6cef9a83152256966b259881521c159b0cf21a8))
- **link-in-text-block:** don't match style or script text ([#3775](https://github.com/dequelabs/axe-core/issues/3775)) ([ab877f9](https://github.com/dequelabs/axe-core/commit/ab877f9d709205c2dadffc656f82dc631b66687b))
- prevent undefined relatedNodes from halting axe ([#3778](https://github.com/dequelabs/axe-core/issues/3778)) ([efefb18](https://github.com/dequelabs/axe-core/commit/efefb18f720590369a97c2937331f4e2e33ef6a5))

### [4.5.1](https://github.com/dequelabs/axe-core/compare/v4.5.0...v4.5.1) (2022-11-01)

### Bug Fixes
Expand Down
2 changes: 1 addition & 1 deletion bower.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "axe-core",
"version": "4.5.1",
"version": "4.5.2",
"contributors": [
{
"name": "David Sturley",
Expand Down
2 changes: 2 additions & 0 deletions lib/checks/aria/aria-required-children.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
"grid",
"list",
"listbox",
"menu",
"menubar",
"table",
"tablist",
"tree",
Expand Down
21 changes: 16 additions & 5 deletions lib/checks/color/link-in-text-block-evaluate.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ function linkInTextBlockEvaluate(node, options) {
// Compute contrasts, giving preference to foreground color and doing as little work as possible
var textContrast =
nodeColor && parentColor ? getContrast(nodeColor, parentColor) : undefined;
if (textContrast) {
textContrast = Math.floor(textContrast * 100) / 100;
}

if (textContrast && textContrast >= requiredContrastRatio) {
return true;
Expand All @@ -61,6 +64,10 @@ function linkInTextBlockEvaluate(node, options) {
? getContrast(nodeBackgroundColor, parentBackgroundColor)
: undefined;

if (backgroundContrast) {
backgroundContrast = Math.floor(backgroundContrast * 100) / 100;
}

if (backgroundContrast && backgroundContrast >= requiredContrastRatio) {
return true;
}
Expand All @@ -80,13 +87,17 @@ function linkInTextBlockEvaluate(node, options) {
}

// Report bgContrast only if the background changes but text color stays the same
if (textContrast === 1.0 && backgroundContrast > 1.0) {
if (textContrast === 1 && backgroundContrast > 1) {
this.data({
messageKey: 'bgContrast',
contrastRatio: backgroundContrast,
requiredContrastRatio,
nodeBackgroundColor,
parentBackgroundColor
nodeBackgroundColor: nodeBackgroundColor
? nodeBackgroundColor.toHexString()
: undefined,
parentBackgroundColor: parentBackgroundColor
? parentBackgroundColor.toHexString()
: undefined
});
return false;
}
Expand All @@ -95,8 +106,8 @@ function linkInTextBlockEvaluate(node, options) {
messageKey: 'fgContrast',
contrastRatio: textContrast,
requiredContrastRatio,
nodeColor,
parentColor
nodeColor: nodeColor ? nodeColor.toHexString() : undefined,
parentColor: parentColor ? parentColor.toHexString() : undefined
});
return false;
}
Expand Down
128 changes: 93 additions & 35 deletions lib/commons/dom/create-grid.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
/* eslint no-bitwise: 0 */
import isVisibleOnScreen from './is-visible-on-screen';
import { getBoundingRect } from '../math/get-bounding-rect';
import { isPointInRect } from '../math/is-point-in-rect';
import VirtualNode from '../../core/base/virtual-node/virtual-node';
import { getNodeFromTree, getScroll, isShadowRoot } from '../../core/utils';
import constants from '../../core/constants';
import cache from '../../core/base/cache';
import assert from '../../core/utils/assert';

/**
* Setup the 2d grid and add every element to it, even elements not
Expand All @@ -12,10 +14,7 @@ import cache from '../../core/base/cache';
*/
export default function createGrid(
root = document.body,
rootGrid = {
container: null,
cells: []
},
rootGrid,
parentVNode = null
) {
// Prevent multiple calls per run
Expand All @@ -34,13 +33,11 @@ export default function createGrid(
}

vNode._stackingOrder = [0];
rootGrid ??= new Grid();
addNodeToGrid(rootGrid, vNode);

if (getScroll(vNode.actualNode)) {
const subGrid = {
container: vNode,
cells: []
};
const subGrid = new Grid(vNode);
vNode._subGrid = subGrid;
}
}
Expand Down Expand Up @@ -76,10 +73,7 @@ export default function createGrid(
const grid = scrollRegionParent ? scrollRegionParent._subGrid : rootGrid;

if (getScroll(vNode.actualNode)) {
const subGrid = {
container: vNode,
cells: []
};
const subGrid = new Grid(vNode);
vNode._subGrid = subGrid;
}

Expand Down Expand Up @@ -323,36 +317,100 @@ function findScrollRegionParent(vNode, parentVNode) {
* @param {VirtualNode}
*/
function addNodeToGrid(grid, vNode) {
const gridSize = constants.gridSize;
vNode.clientRects.forEach(rect => {
if (rect.right <= 0 || rect.bottom <= 0) {
return;
}
// save a reference to where this element is in the grid so we
// can find it even if it's in a subgrid
vNode._grid ??= grid;
const x = rect.left;
const y = rect.top;
const gridRect = grid.getGridPositionOfRect(rect);
grid.loopGridPosition(gridRect, gridCell => {
if (!gridCell.includes(vNode)) {
gridCell.push(vNode);
}
});
});
}

// "| 0" is a faster way to do Math.floor
// @see https://jsperf.com/math-floor-vs-math-round-vs-parseint/152
const startRow = (y / gridSize) | 0;
const startCol = (x / gridSize) | 0;
const endRow = ((y + rect.height) / gridSize) | 0;
const endCol = ((x + rect.width) / gridSize) | 0;
class Grid {
constructor(container = null) {
this.container = container;
this.cells = [];
}

grid.numCols = Math.max(grid.numCols ?? 0, endCol);
/**
* Convert x or y coordinate from rect, to a position in the grid
* @param {number}
* @returns {number}
*/
toGridIndex(num) {
return Math.floor(num / constants.gridSize);
}

for (let row = startRow; row <= endRow; row++) {
grid.cells[row] = grid.cells[row] || [];
/**
* Return an an array of nodes available at a particular grid coordinate
* @param {DOMPoint} gridPosition
* @returns {Array<AbstractVirtualNode>}
*/
getCellFromPoint({ x, y }) {
assert(this.boundaries, 'Grid does not have cells added');
const rowIndex = this.toGridIndex(y);
const colIndex = this.toGridIndex(x);
assert(
isPointInRect({ y: rowIndex, x: colIndex }, this.boundaries),
'Element midpoint exceeds the grid bounds'
);
const row = this.cells[rowIndex - this.cells._negativeIndex] ?? [];
return row[colIndex - row._negativeIndex] ?? [];
}

for (let col = startCol; col <= endCol; col++) {
grid.cells[row][col] = grid.cells[row][col] || [];
/**
* Loop over all cells within the gridPosition rect
* @param {DOMRect} gridPosition
* @param {Function} callback
*/
loopGridPosition(gridPosition, callback) {
const { left, right, top, bottom } = gridPosition;
if (this.boundaries) {
gridPosition = getBoundingRect(this.boundaries, gridPosition);
}
this.boundaries = gridPosition;

if (!grid.cells[row][col].includes(vNode)) {
grid.cells[row][col].push(vNode);
}
}
loopNegativeIndexMatrix(this.cells, top, bottom, (gridRow, row) => {
loopNegativeIndexMatrix(gridRow, left, right, (gridCell, col) => {
callback(gridCell, { row, col });
});
});
}

/**
* Scale the rect to the position within the grid
* @param {DOMRect} clientOrBoundingRect
* @param {number} margin Offset outside the rect, default 0
* @returns {DOMRect} gridPosition
*/
getGridPositionOfRect({ top, right, bottom, left }, margin = 0) {
top = this.toGridIndex(top - margin);
right = this.toGridIndex(right + margin - 1);
bottom = this.toGridIndex(bottom + margin - 1);
left = this.toGridIndex(left - margin);
return new window.DOMRect(left, top, right - left, bottom - top);
}
}

// handle negative row/col values
function loopNegativeIndexMatrix(matrix, start, end, callback) {
matrix._negativeIndex ??= 0;
// Shift the array when start is negative
if (start < matrix._negativeIndex) {
for (let i = 0; i < matrix._negativeIndex - start; i++) {
matrix.splice(0, 0, []);
}
});
matrix._negativeIndex = start;
}

const startOffset = start - matrix._negativeIndex;
const endOffset = end - matrix._negativeIndex;
for (let index = startOffset; index <= endOffset; index++) {
matrix[index] ??= [];
callback(matrix[index], index + matrix._negativeIndex);
}
}
46 changes: 14 additions & 32 deletions lib/commons/dom/find-nearby-elms.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,32 @@ import createGrid from './create-grid';
import { memoize } from '../../core/utils';

export default function findNearbyElms(vNode, margin = 0) {
/*eslint no-bitwise: 0*/
const gridSize = createGrid();
const selfIsFixed = hasFixedPosition(vNode);
createGrid(); // Ensure grid exists
if (!vNode._grid?.cells?.length) {
return []; // Elements not in the grid don't have ._grid
}

const rect = vNode.boundingClientRect;
const gridCells = vNode._grid.cells;
const boundaries = {
topRow: ((rect.top - margin) / gridSize) | 0,
bottomRow: ((rect.bottom + margin) / gridSize) | 0,
leftCol: ((rect.left - margin) / gridSize) | 0,
rightCol: ((rect.right + margin) / gridSize) | 0
};
const grid = vNode._grid;
const selfIsFixed = hasFixedPosition(vNode);
const gridPosition = grid.getGridPositionOfRect(rect, margin);

const neighbors = [];
loopGridCells(gridCells, boundaries, vNeighbor => {
if (
vNeighbor &&
vNeighbor !== vNode &&
!neighbors.includes(vNeighbor) &&
selfIsFixed === hasFixedPosition(vNeighbor)
) {
neighbors.push(vNeighbor);
grid.loopGridPosition(gridPosition, vNeighbors => {
for (const vNeighbor of vNeighbors) {
if (
vNeighbor &&
vNeighbor !== vNode &&
!neighbors.includes(vNeighbor) &&
selfIsFixed === hasFixedPosition(vNeighbor)
) {
neighbors.push(vNeighbor);
}
}
});

return neighbors;
}

function loopGridCells(gridCells, boundaries, cb) {
const { topRow, bottomRow, leftCol, rightCol } = boundaries;
for (let row = topRow; row <= bottomRow; row++) {
for (let col = leftCol; col <= rightCol; col++) {
// Don't loop on elements outside the grid
const length = gridCells[row]?.[col]?.length ?? -1;
for (let i = 0; i < length; i++) {
cb(gridCells[row][col][i]);
}
}
}
}

const hasFixedPosition = memoize(vNode => {
if (!vNode) {
return false;
Expand Down
Loading

0 comments on commit 8212b9e

Please sign in to comment.