Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move aria-expanded to 1st cell in each row if only cells can be focused #369

Merged
merged 2 commits into from
Apr 21, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions examples/treegrid/css/treegrid-row-nav-primary-1.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
table-layout: fixed;
}

#treegrid tr {
cursor: default;
}

#treegrid-col1, #treegrid-col3 {
width: 30%;
}
Expand Down Expand Up @@ -67,6 +71,7 @@
}

#treegrid tr[aria-expanded] > td:first-child::before {
cursor: pointer;
/* Load both right away so there is no lag when we need the other */
background-image: url("expand-icon.svg"), url("expand-icon-highlighted.svg");
background-repeat: no-repeat;
Expand Down
80 changes: 69 additions & 11 deletions examples/treegrid/js/treegrid-row-nav-primary-1.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ function onReady (treegrid, doAllowRowFocus, doStartRowFocus) {
}
else {
setTabIndexForCellsInRow(rows[index], -1);
moveAriaExpandedToFirstCell(rows[index]);
}
propagateExpandedToFirstCell(rows[index]);
}

if (doStartRowFocus) {
Expand Down Expand Up @@ -149,7 +149,11 @@ function onReady (treegrid, doAllowRowFocus, doStartRowFocus) {
// The row with focus is the row that either has focus or an element
// inside of it has focus
function getRowWithFocus () {
var possibleRow = document.activeElement;
return getContainingRow(document.activeElement);
}

function getContainingRow (start) {
var possibleRow = start;
if (treegrid.contains(possibleRow)) {
while (possibleRow !== treegrid) {
if (possibleRow.localName === 'tr') {
Expand Down Expand Up @@ -304,12 +308,12 @@ function onReady (treegrid, doAllowRowFocus, doStartRowFocus) {
var cols = getNavigableCols(currentRow);
var currentCol = getColWithFocus(currentRow);
if (currentCol === cols[0] && currentRow.hasAttribute('aria-expanded')) {
changeExpanded(currentRow.getAttribute('aria-expanded') === 'false');
changeExpanded(isExpanded(currentRow));
}
}

function changeExpanded (doExpand) {
var currentRow = getRowWithFocus();
function changeExpanded (doExpand, row) {
var currentRow = row || getRowWithFocus();
if (!currentRow) {
return;
}
Expand Down Expand Up @@ -340,24 +344,40 @@ function onReady (treegrid, doAllowRowFocus, doStartRowFocus) {
}
}
if (didChange) {
currentRow.setAttribute('aria-expanded', doExpand);
propagateExpandedToFirstCell(currentRow);
setAriaExpanded(currentRow, doExpand);
return true;
}
}

// Mirror aria-expanded from the row to the first cell in that row
// (TBD is this a good idea? How else will screen reader user hear
// that the cell represents the opportunity to collapse/expand rows?)
function propagateExpandedToFirstCell (row) {
function moveAriaExpandedToFirstCell (row) {
var expandedValue = row.getAttribute('aria-expanded');
var firstCell = getNavigableCols(row)[0];
if (expandedValue) {
firstCell.setAttribute('aria-expanded', expandedValue);
row.removeAttribute('aria-expanded');
}
else {
firstCell.removeAttribute('aria-expanded');
}
}

function getAriaExpandedElem(row) {
return doAllowRowFocus ? row : getNavigableCols(row)[0];
}

function setAriaExpanded (row, doExpand) {
var elem = getAriaExpandedElem(row);
elem.setAttribute('aria-expanded', doExpand);
}

function isExpandable (row) {
var elem = getAriaExpandedElem(row);
return elem.hasAttribute('aria-expanded');
}

function isExpanded (row) {
var elem = getAriaExpandedElem(row);
return elem.getAttribute('aria-expanded') === 'true';
}

function onKeyDown (event) {
Expand Down Expand Up @@ -441,8 +461,46 @@ function onReady (treegrid, doAllowRowFocus, doStartRowFocus) {
event.preventDefault();
}

// Toggle row expansion if the click is over the expando triangle
// Since the triangle is a pseudo element we can't bind an event listener
// to it. Another option is to have an actual element with role="presentation"
function onClick (event) {
var target = event.target;
if (target.localName !== 'td') {
return;
}

var row = getContainingRow(event.target);
if (!isExpandable(row)) {
return;
}

// Determine if mouse coordinate is just to the left of the start of text
var range = document.createRange();
range.selectNodeContents(target.firstChild);
var left = range.getBoundingClientRect().left;
var EXPANDO_WIDTH = 20;

if (event.clientX < left && event.clientX > left - EXPANDO_WIDTH) {
changeExpanded(!isExpanded(row), row);
}
}

// Double click on row toggles expansion
function onDoubleClick (event) {
var row = getContainingRow(event.target);
if (row) {
if (isExpandable(row)) {
changeExpanded(!isExpanded(row), row);
}
event.preventDefault();
}
}

initAttributes();
treegrid.addEventListener('keydown', onKeyDown);
treegrid.addEventListener('click', onClick);
treegrid.addEventListener('dblclick', onDoubleClick);
// Polyfill for focusin necessary for Firefox < 52
window.addEventListener(window.onfocusin ? 'focusin' : 'focus',
onFocusIn, true);
Expand Down
2 changes: 1 addition & 1 deletion examples/treegrid/treegrid-row-nav-primary-1.html
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ <h2>Accessibility Features</h2>
<li>aria-level: used to set the current level of an item, 1, 2, 3, etc.</li>
<li>aria-posinset, aria-setsize: used on a row to indicate the position of an item within it's local group, such as item 3 of 5. Unfortunately, <a href="https://github.com/w3c/aria/issues/558">aria-posinset and aria-setsize are not currently legal in the spec (bug filed)</a>. Therefore, <a href="https://bugzilla.validator.nu/show_bug.cgi?id=1033">aria-posinset and aria-setsize are not currently supported on role="row" in the Nu Validator (bug filed)</a>. We'll need to work that in as this is the most practical way of implementing ARIA in many existing treegrid libraries (as opposed to rearchitecting the
DOM structure to contain rowgroups). Note that the <a href="https://github.com/w3c/aria/issues/553">ARIA spec's text for rowgroup states that it cannot be parented by a treegrid (bug filed)</a>.</li>
<li>aria-expanded (tristate): this attribute must be removed (not present) if the item cannot be expanded. For expandable items, the value is "true" or "false"</li>
<li>aria-expanded (tristate): this attribute must be removed (not present) if the item cannot be expanded. For expandable items, the value is "true" or "false".<br>IMPORTANT: aria-expanded is set on the row unless rows cannot receive focus, because it is an indicator to the user that something is actionable. If only the cell can get focused, then pressing Enter on the cell is the only way to indicate this, so the attribute needs to go there so that screen readers will announce it. It is not added to the cell if rows can receive focus, as this provides a confusing/redundant experience where both the row and first cell announce the expanded state. In this example, JavaScript is used to move the aria-expanded attribute down to the first cell in the ?cell=force version.</li>
<li>aria-hidden: set to "true" for child items that are currently hidden because the parent is collapsed.</li>
<li>aria-readonly: in ARIA 1.0, a grid/treegrid is editable by default. However, there is no default in ARIA 1.1. Firefox currently implements the ARIA 1.0 concept for this, which means that "editable" is read for every cell unless aria-readonly="true" is used on the treegrid. There needs to be some follow up with user agent developers on this, as ARIA 1.1 seems to be treating it more a tristate (don't care, false, true). In addition, <a href="https://github.com/w3c/aria/issues/550">a bug was filed on the spec regarding aria-readonly but it is possibly invalid</a>.</li>
<li>tabindex is set in the JS, as per the usual roving tabindex methodology. Specifically, we use tabindex="0" for the current item so that it is the subitem that gets focused if user tabs out and back in, and tabindex="-1" for all items where we want click-to-focus behavior enabled.</li>
Expand Down