-
Notifications
You must be signed in to change notification settings - Fork 791
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(td-has-headers): greatly improve performance of td-has-headers ru…
…le (#1887) * fix(td-has-headers): greatly improve performance of td-has-headers rule * remove console.log * add tests
- Loading branch information
Showing
12 changed files
with
201 additions
and
21 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
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 |
---|---|---|
@@ -1,29 +1,73 @@ | ||
/* global table */ | ||
|
||
/** | ||
* Loop through the table grid looking for headers and caching the result. | ||
* @param {String} headerType The type of header to look for ("row" or "col") | ||
* @param {Object} position The position of the cell to start looking | ||
* @param {Array} tablegrid A matrix of the table obtained using axe.commons.table.toGrid | ||
* @return {Array<HTMLTableCellElement>} Array of HTMLTableCellElements that are headers | ||
*/ | ||
function traverseForHeaders(headerType, position, tableGrid) { | ||
const property = headerType === 'row' ? '_rowHeaders' : '_colHeaders'; | ||
const predicate = | ||
headerType === 'row' ? table.isRowHeader : table.isColumnHeader; | ||
const rowEnd = headerType === 'row' ? position.y : 0; | ||
const colEnd = headerType === 'row' ? 0 : position.x; | ||
|
||
let headers; | ||
const cells = []; | ||
for (let row = position.y; row >= rowEnd && !headers; row--) { | ||
for (let col = position.x; col >= colEnd; col--) { | ||
const cell = tableGrid[row] ? tableGrid[row][col] : undefined; | ||
|
||
if (!cell) { | ||
continue; | ||
} | ||
|
||
// stop traversing once we've found a cache | ||
const vNode = axe.utils.getNodeFromTree(cell); | ||
if (vNode[property]) { | ||
headers = vNode[property]; | ||
break; | ||
} | ||
|
||
cells.push(cell); | ||
} | ||
} | ||
|
||
// need to check that the cells we've traversed are headers | ||
headers = (headers || []).concat(cells.filter(predicate)); | ||
|
||
// cache results | ||
cells.forEach(tableCell => { | ||
const vNode = axe.utils.getNodeFromTree(tableCell); | ||
vNode[property] = headers; | ||
}); | ||
|
||
return headers; | ||
} | ||
|
||
/** | ||
* Get any associated table headers for a `HTMLTableCellElement` | ||
* @method getHeaders | ||
* @memberof axe.commons.table | ||
* @instance | ||
* @param {HTMLTableCellElement} cell The cell of which to get headers | ||
* @param {Array} [tablegrid] A matrix of the table obtained using axe.commons.table.toGrid | ||
* @return {Array<HTMLTableCellElement>} Array of headers associated to the table cell | ||
*/ | ||
table.getHeaders = function(cell) { | ||
table.getHeaders = function(cell, tableGrid) { | ||
if (cell.hasAttribute('headers')) { | ||
return commons.dom.idrefs(cell, 'headers'); | ||
} | ||
|
||
var tableGrid = commons.table.toGrid(commons.dom.findUp(cell, 'table')); | ||
var position = commons.table.getCellPosition(cell, tableGrid); | ||
if (!tableGrid) { | ||
tableGrid = commons.table.toGrid(commons.dom.findUp(cell, 'table')); | ||
} | ||
const position = commons.table.getCellPosition(cell, tableGrid); | ||
|
||
// TODO: RTL text | ||
var rowHeaders = table | ||
.traverse('left', position, tableGrid) | ||
.filter(cell => table.isRowHeader(cell)); | ||
|
||
var colHeaders = table | ||
.traverse('up', position, tableGrid) | ||
.filter(cell => table.isColumnHeader(cell)); | ||
const rowHeaders = traverseForHeaders('row', position, tableGrid); | ||
const colHeaders = traverseForHeaders('col', position, tableGrid); | ||
|
||
return [].concat(rowHeaders, colHeaders).reverse(); | ||
}; |
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,17 @@ | ||
/** | ||
* Memoize a function. | ||
* @method memoize | ||
* @memberof axe.utils | ||
* @param {Function} fn Function to memoize | ||
* @return {Function} | ||
*/ | ||
axe._memoizedFns = []; | ||
axe.utils.memoize = function(fn) { | ||
// keep track of each function that is memoized so it can be cleared at | ||
// the end of a run. each memoized function has its own cache, so there is | ||
// no method to clear all memoized caches. instead, we have to clear each | ||
// individual memoized function ourselves. | ||
const memoized = axe.imports.memoize(fn); | ||
axe._memoizedFns.push(memoized); | ||
return memoized; | ||
}; |
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,32 @@ | ||
describe('axe.utils.memoize', function() { | ||
'use strict'; | ||
|
||
var orig = axe.imports.memoize; | ||
var memoizedFns; | ||
|
||
beforeEach(function() { | ||
memoizedFns = axe._memoizedFns.slice(); | ||
}); | ||
|
||
afterEach(function() { | ||
axe.imports.memoize = orig; | ||
axe._memoizedFns = memoizedFns; | ||
}); | ||
|
||
it('should call imports.memoize', function() { | ||
var called = false; | ||
axe.imports.memoize = function() { | ||
called = true; | ||
}; | ||
|
||
axe.utils.memoize(function() {}); | ||
assert.isTrue(called); | ||
}); | ||
|
||
it('should add the function to axe._memoizedFns', function() { | ||
axe._memoizedFns.length = 0; | ||
|
||
axe.utils.memoize(function myFn() {}); | ||
assert.equal(axe._memoizedFns.length, 1); | ||
}); | ||
}); |