-
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.
feat(td-headers-attr): report headers attribute referencing other <td…
…> elements as unsupported (#4589) Fix for the header attribute check. The check will report cells that references other `<td>` elements. Added unit and integrations tests as requested in corresponding issue. Closes: [#3987](#3987) --------- Co-authored-by: Dan Bjorge <[email protected]>
- Loading branch information
1 parent
b7736de
commit ec7c6c8
Showing
9 changed files
with
160 additions
and
48 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 |
---|---|---|
@@ -1,63 +1,79 @@ | ||
import { tokenList } from '../../core/utils'; | ||
import { isVisibleToScreenReaders } from '../../commons/dom'; | ||
import { getRole } from '../../commons/aria'; | ||
|
||
// Order determines the priority of reporting | ||
// Only if 0 of higher issues exists will the next be reported | ||
const messageKeys = [ | ||
'cell-header-not-in-table', | ||
'cell-header-not-th', | ||
'header-refs-self', | ||
'empty-hdrs' // incomplete | ||
]; | ||
const [notInTable, notTh, selfRef, emptyHdrs] = messageKeys; | ||
|
||
export default function tdHeadersAttrEvaluate(node) { | ||
const cells = []; | ||
const reviewCells = []; | ||
const badCells = []; | ||
|
||
const cellRoleById = {}; | ||
for (let rowIndex = 0; rowIndex < node.rows.length; rowIndex++) { | ||
const row = node.rows[rowIndex]; | ||
|
||
for (let cellIndex = 0; cellIndex < row.cells.length; cellIndex++) { | ||
cells.push(row.cells[cellIndex]); | ||
const cell = row.cells[cellIndex]; | ||
cells.push(cell); | ||
|
||
// Save header id to set if it's th or td with roles columnheader/rowheader | ||
const cellId = cell.getAttribute('id'); | ||
if (cellId) { | ||
cellRoleById[cellId] = getRole(cell); | ||
} | ||
} | ||
} | ||
|
||
const ids = cells | ||
.filter(cell => cell.getAttribute('id')) | ||
.map(cell => cell.getAttribute('id')); | ||
|
||
const badCells = { | ||
[selfRef]: new Set(), | ||
[notInTable]: new Set(), | ||
[notTh]: new Set(), | ||
[emptyHdrs]: new Set() | ||
}; | ||
cells.forEach(cell => { | ||
let isSelf = false; | ||
let notOfTable = false; | ||
|
||
if (!cell.hasAttribute('headers') || !isVisibleToScreenReaders(cell)) { | ||
return; | ||
} | ||
|
||
const headersAttr = cell.getAttribute('headers').trim(); | ||
if (!headersAttr) { | ||
return reviewCells.push(cell); | ||
badCells[emptyHdrs].add(cell); | ||
return; | ||
} | ||
|
||
const cellId = cell.getAttribute('id'); | ||
// Get a list all the values of the headers attribute | ||
const headers = tokenList(headersAttr); | ||
|
||
if (headers.length !== 0) { | ||
// Check if the cell's id is in this list | ||
if (cell.getAttribute('id')) { | ||
isSelf = headers.indexOf(cell.getAttribute('id').trim()) !== -1; | ||
headers.forEach(headerId => { | ||
if (cellId && headerId === cellId) { | ||
// Header references its own cell | ||
badCells[selfRef].add(cell); | ||
} else if (!cellRoleById[headerId]) { | ||
// Header references a cell that is not in the table | ||
badCells[notInTable].add(cell); | ||
} else if ( | ||
!['columnheader', 'rowheader'].includes(cellRoleById[headerId]) | ||
) { | ||
// Header references a cell that is not a row or column header | ||
badCells[notTh].add(cell); | ||
} | ||
}); | ||
}); | ||
|
||
// Check if the headers are of cells inside the table | ||
notOfTable = headers.some(header => !ids.includes(header)); | ||
|
||
if (isSelf || notOfTable) { | ||
badCells.push(cell); | ||
for (const messageKey of messageKeys) { | ||
if (badCells[messageKey].size > 0) { | ||
this.relatedNodes([...badCells[messageKey]]); | ||
if (messageKey === emptyHdrs) { | ||
return undefined; | ||
} | ||
this.data({ messageKey }); | ||
return false; | ||
} | ||
}); | ||
|
||
if (badCells.length > 0) { | ||
this.relatedNodes(badCells); | ||
return false; | ||
} | ||
|
||
if (reviewCells.length) { | ||
this.relatedNodes(reviewCells); | ||
return undefined; | ||
} | ||
|
||
return true; | ||
} |
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
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
11 changes: 9 additions & 2 deletions
11
test/integration/rules/td-headers-attr/td-headers-attr.json
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,6 +1,13 @@ | ||
{ | ||
"description": "td-headers-attr test", | ||
"rule": "td-headers-attr", | ||
"violations": [["#fail1"], ["#fail2"], ["#fail3"]], | ||
"passes": [["#pass1"], ["#pass2"], ["#pass3"], ["#pass4"]] | ||
"violations": [ | ||
["#fail1"], | ||
["#fail2"], | ||
["#fail3"], | ||
["#fail4"], | ||
["#fail5"], | ||
["#fail6"] | ||
], | ||
"passes": [["#pass1"], ["#pass2"], ["#pass3"], ["#pass4"], ["#pass5"]] | ||
} |