Skip to content

Commit

Permalink
update rule and test cases #1472
Browse files Browse the repository at this point in the history
  • Loading branch information
shunguoy committed Aug 21, 2023
1 parent ff2c3d9 commit 18533ba
Show file tree
Hide file tree
Showing 5 changed files with 293 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1097,60 +1097,116 @@ export class RPTUtil {
}
if (!isComplexTable && trNodeCount !== 0) {
// a table with headers not located in the first row or first column
isComplexTable = thNodeCount > 0 && !RPTUtil.isTableHeaderInFirstRowOrColumn(table);
isComplexTable = thNodeCount > 0 && !RPTUtil.tableHeaderExists(table);
}
}
table.RPTUtil_isComplexDataTable = isComplexTable;

return isComplexTable;
}

// Return true if a table cell is hidden or contain no data: <td></td>
public static isTableCellEmpty(cell) {
if (!cell || !VisUtil.isNodeVisible(cell) || cell.innerHTML.trim().length === 0)
return true;

return false;
}

// Return true if a table row is hidden or contain no data: <tr /> or <tr><td></td><td></td></tr>
public static isTableRowEmpty(row) {
if (!row || !row.cells || row.cells.length === 0 || !VisUtil.isNodeVisible(row))
return true;

let passed = true; //empty
for (let c=0; passed && c < row.cells.length; c++) {
let cell = row.cells[c];
passed = RPTUtil.isTableCellEmpty(cell);
}

return passed;
}

// Return true if a table's header is in the first row or column
public static isTableHeaderInFirstRowOrColumn(ruleContext) {
public static tableHeaderExists(ruleContext) {

let passed = true;
let rows = ruleContext.rows;
if (!rows || rows.length === 0)
return null;

//case 1: header is in the very first row with data
//get the first row with data
let passed = true;
let firstRow = rows[0];
for (let r=0; passed && r < rows.length; r++) {
firstRow = rows[r];
passed = RPTUtil.isTableRowEmpty(firstRow);
}

if (rows != null && rows.length > 0) {
let firstRow = rows[0];
// Check if the cells with data in the first row are all TH's
//passed = firstRow.cells.length > 0 && RPTUtil.getChildByTagHidden(firstRow, "td", false, true).length === 0;
if (!firstRow.cells)
passed = false;
else {
for (let c=0; passed && c < firstRow.cells.length; c++) {
let cell = firstRow.cells[c];
passed = !VisUtil.isNodeVisible(cell) || (cell.innerHTML.trim().length > 0 && cell.nodeName.toLowerCase() === 'th');
}
}

// If the first row isn't a header row, try the first column
if (!passed) {
// Assume that the first column has all TH's or a TD without data in the first column.
passed = true;
for (let i = 0; passed && i < rows.length; ++i) {
// ignore the rows from tfoot
if (rows[i].parentNode && rows[i].parentNode.nodeName.toLowerCase() === 'tfoot') continue;
// If no cells in this row, or no data at all, that's okay too.
passed = !rows[i].cells ||
rows[i].cells.length === 0 ||
rows[i].cells[0].innerHTML.trim().length === 0 ||
rows[i].cells[0].nodeName.toLowerCase() != "td";
}
}
if (!passed) {
// Special case - both first row and first column are headers, but they did not use
// a th for the upper-left cell
passed = true;
for (let i = 1; passed && i < firstRow.cells.length; ++i) {
passed = firstRow.cells[i].nodeName.toLowerCase() != "td";
}
for (let i = 1; passed && i < rows.length; ++i) {
// If no cells in this row, that's okay too.
passed = !rows[i].cells ||
rows[i].cells.length === 0 ||
rows[i].cells[0].nodeName.toLowerCase() != "td";
}
//table contain no data: <table><tr><td></td><td></td></tr></table>
if (passed)
return null;

// Check if the cells with data in the first data row are all TH's
//passed = firstRow.cells.length > 0 && RPTUtil.getChildByTagHidden(firstRow, "td", false, true).length === 0;
passed = true;
for (let c=0; passed && c < firstRow.cells.length; c++) {
let cell = firstRow.cells[c];
passed = !RPTUtil.isTableCellEmpty(cell) && cell.nodeName.toLowerCase() === 'th';
}

if (passed)
return true;

//case 2: header is in the very last/bottom row with data
//get the last row with data
passed = true;
let lastRow = rows[rows.length-1];
for (let r=rows.length; passed && r >= 0; r++) {
lastRow = rows[r];
passed = RPTUtil.isTableRowEmpty(lastRow);
}

if (passed) //shouldn't happen!
return true;

// Check if the cells with data in the first data row are all TH's
//passed = firstRow.cells.length > 0 && RPTUtil.getChildByTagHidden(firstRow, "td", false, true).length === 0;
passed = true;
for (let c=0; passed && c < lastRow.cells.length; c++) {
let cell = lastRow.cells[c];
passed = !RPTUtil.isTableCellEmpty(cell) && cell.nodeName.toLowerCase() === 'th';
}

if (passed)
return true;

// Case 3: header is in the first data columns
// Assume that the first column has all TH's or a TD without data in the first column.
passed = true;
for (let i = 0; passed && i < rows.length; ++i) {
// ignore the rows from tfoot
if (rows[i].parentNode && rows[i].parentNode.nodeName.toLowerCase() === 'tfoot') continue;
// If no cells in this row, or no data at all, that's okay too.
passed = !rows[i].cells ||
rows[i].cells.length === 0 ||
rows[i].cells[0].innerHTML.trim().length === 0 ||
rows[i].cells[0].nodeName.toLowerCase() != "td";
}


if (!passed) {
// Special case - both first row and first column are headers, but they did not use
// a th for the upper-left cell
passed = true;
for (let i = 1; passed && i < firstRow.cells.length; ++i) {
passed = firstRow.cells[i].nodeName.toLowerCase() != "td";
}
for (let i = 1; passed && i < rows.length; ++i) {
// If no cells in this row, that's okay too.
passed = !rows[i].cells ||
rows[i].cells.length === 0 ||
rows[i].cells[0].nodeName.toLowerCase() != "td";
}
}
return passed;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,13 @@ export let table_headers_exists: Rule = {
const ruleContext = context["dom"].node as HTMLTableElement;
// If this is a layout table or there are no rows, the rule does not apply.
let rows = ruleContext.rows;
if (!RPTUtil.isDataTable(ruleContext) || rows == null || rows.length == 0)
if (!RPTUtil.isDataTable(ruleContext) || rows === null || rows.length === 0)
return null;

let passed = RPTUtil.isTableHeaderInFirstRowOrColumn(ruleContext);

let passed = RPTUtil.tableHeaderExists(ruleContext);
if (passed === null)
return;

if (!passed) {
return RuleFail("Fail_1");
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html>
<!--
/******************************************************************************
Copyright:: 2020- IBM, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*****************************************************************************/
-->

<head>
<META http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Data table</title>
</head>
<body>
<table>
</table>

<table>
<tr><td></td><td></td></tr>
<tr><td></td><td></td></tr>
<tr><td></td><td></td></tr>
</table>

<script>
UnitTest = {
ruleIds: ["table_headers_exists"],
results: [

]
};
</script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html>
<!--
/******************************************************************************
Copyright:: 2020- IBM, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*****************************************************************************/
-->

<head>
<META http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Data table</title>
</head>
<body>
<table>
<caption>
Council budget 2018
</caption>
<tbody>
<tr>
<td>Donuts</th>
<td>3,000</td>
<td>2,000</td>
</tr>
<tr>
<td>Stationery</th>
<td>18,000</td>
<td>8,000</td>
</tr>
<tr>
<th >Items</th>
<th >Expenditure</th>
<th >Other</th>
</tr>
<tr><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td></tr>
</tbody>
</table>
<script>
UnitTest = {
ruleIds: ["table_headers_exists"],
results: [
{
"ruleId": "table_headers_exists",
"value": [
"INFORMATION",
"PASS"
],
"path": {
"dom": "/html[1]/body[1]/table[1]",
"aria": "/document[1]/table[1]"
},
"reasonId": "Pass_0",
"message": "Rule Passed",
"messageArgs": [],
"apiArgs": [],
"category": "Accessibility"
}
]
};
</script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html>
<!--
/******************************************************************************
Copyright:: 2020- IBM, Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*****************************************************************************/
-->

<head>
<META http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Data table</title>
</head>
<body>
<table>
<caption>
Council budget 2018
</caption>
<tbody>
<tr><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td></tr>
<tr>
<th>Items</th>
<th>Expenditure</th>
<th>Other</th>
</tr>
<tr>
<td>Donuts</th>
<td>3,000</td>
<td>2,000</td>
</tr>
<tr>
<td>Stationery</th>
<td>18,000</td>
<td>8,000</td>
</tr>
</tbody>
</table>
<script>
UnitTest = {
ruleIds: ["table_headers_exists"],
results: [
{
"ruleId": "table_headers_exists",
"value": [
"INFORMATION",
"PASS"
],
"path": {
"dom": "/html[1]/body[1]/table[1]",
"aria": "/document[1]/table[1]"
},
"reasonId": "Pass_0",
"message": "Rule Passed",
"messageArgs": [],
"apiArgs": [],
"category": "Accessibility"
}
]
};
</script>
</body>
</html>

0 comments on commit 18533ba

Please sign in to comment.