Added Aria roles and properties to the CellTree Review at http://gwt-code-reviews.appspot.com/1776803 git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@11211 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/cellview/client/CellTree.java b/user/src/com/google/gwt/user/cellview/client/CellTree.java index da94e63..7b12a0a 100644 --- a/user/src/com/google/gwt/user/cellview/client/CellTree.java +++ b/user/src/com/google/gwt/user/cellview/client/CellTree.java
@@ -45,6 +45,7 @@ import com.google.gwt.user.client.ui.HasAnimation; import com.google.gwt.user.client.ui.SimplePanel; import com.google.gwt.view.client.TreeViewModel; +import com.google.gwt.aria.client.Roles; import java.util.ArrayList; import java.util.HashSet; @@ -653,6 +654,8 @@ getElement(), rootValue, messages); keyboardSelectedNode = rootNode = root; root.setOpen(true, false); + + Roles.getTreeRole().set(getElement()); } /**
diff --git a/user/src/com/google/gwt/user/cellview/client/CellTreeNodeView.java b/user/src/com/google/gwt/user/cellview/client/CellTreeNodeView.java index a7cd065..95d8573 100644 --- a/user/src/com/google/gwt/user/cellview/client/CellTreeNodeView.java +++ b/user/src/com/google/gwt/user/cellview/client/CellTreeNodeView.java
@@ -15,6 +15,7 @@ */ package com.google.gwt.user.cellview.client; +import com.google.gwt.aria.client.Roles; import com.google.gwt.cell.client.Cell; import com.google.gwt.cell.client.Cell.Context; import com.google.gwt.core.client.GWT; @@ -55,6 +56,7 @@ import com.google.gwt.view.client.SelectionModel; import com.google.gwt.view.client.TreeViewModel; import com.google.gwt.view.client.TreeViewModel.NodeInfo; +import com.google.gwt.aria.client.ExpandedValue; import java.util.ArrayList; import java.util.HashMap; @@ -77,10 +79,10 @@ SafeHtml innerDiv(SafeStyles cssString, String classes, SafeHtml image, String itemValueStyle, SafeHtml cellContents); - @Template("<div><div style=\"{0}\" class=\"{1}\">{2}</div></div>") - SafeHtml outerDiv(SafeStyles cssString, String classes, SafeHtml content); + @Template("<div aria-selected=\"{3}\">" + + "<div style=\"{0}\" class=\"{1}\">{2}</div></div>") + SafeHtml outerDiv(SafeStyles cssString, String classes, SafeHtml content, String ariaSelected); } - /** * The {@link com.google.gwt.view.client.HasData} used to show children. This * class is intentionally static because we might move it to a new @@ -153,7 +155,9 @@ if (isRootNode) { outerClasses.append(topStyle); } - if (selectionModel != null && selectionModel.isSelected(value)) { + boolean isSelected = (selectionModel != null && selectionModel.isSelected(value)); + String ariaSelected = String.valueOf(isSelected); + if (isSelected) { outerClasses.append(selectedStyle); } @@ -182,11 +186,11 @@ SafeHtml innerDiv = template.innerDiv(innerPadding, innerClasses.toString(), image, itemValueStyle, cellBuilder.toSafeHtml()); - SafeStyles outerPadding = SafeStylesUtils.fromTrustedString("padding-" + paddingDirection + ": " + paddingAmount + "px;"); - sb.append(template.outerDiv(outerPadding, outerClasses.toString(), innerDiv)); + sb.append(template.outerDiv(outerPadding, outerClasses.toString(), innerDiv, + ariaSelected)); } } @@ -282,6 +286,7 @@ int len = values.size(); int end = start + len; int childCount = nodeView.getChildCount(); + int setSize = (childCount > len) ? childCount : end; ProvidesKey<C> keyProvider = nodeInfo.getProvidesKey(); Element container = nodeView.ensureChildContainer(); @@ -344,6 +349,7 @@ } else { nodeView.children.add(child); } + child.updateAriaAttributes(setSize); childElem = childElem.getNextSiblingElement(); } @@ -865,6 +871,8 @@ this.value = value; this.messages = messages; setElement(elem); + + Roles.getTreeitemRole().set(getElement()); } public int getChildCount() { @@ -1345,6 +1353,26 @@ listView.setVisibleRange(range.getStart(), pageSize); } + private void updateAriaAttributes(int setSize) { + // Early out if this is a root node. + if (isRootNode()) { + return; + } + + Roles.getTreeitemRole().setAriaSetsizeProperty(getElement(), setSize); + int selectionIndex = parentNode.indexOf(this); + Roles.getTreeitemRole().setAriaPosinsetProperty(getElement(), selectionIndex + 1); + // Set 'aria-expanded' state + // don't set aria-expanded on the leaf nodes + if (isLeaf()) { + Roles.getTreeitemRole().removeAriaExpandedState(getElement()); + } else { + Roles.getTreeitemRole().setAriaExpandedState(getElement(), + ExpandedValue.of(open)); + } + Roles.getTreeitemRole().setAriaLevelProperty(getElement(), this.depth); + } + /** * Update the image based on the current state. * @@ -1371,5 +1399,14 @@ Element oldImg = getImageElement(); oldImg.getParentElement().replaceChild(imageElem, oldImg); + + // Set 'aria-expanded' state + // don't set aria-expanded on the leaf nodes + if (isLeaf()) { + Roles.getTreeitemRole().removeAriaExpandedState(getElement()); + } else { + Roles.getTreeitemRole().setAriaExpandedState(getElement(), + ExpandedValue.of(open)); + } } }
diff --git a/user/test/com/google/gwt/user/cellview/client/CellTreeTest.java b/user/test/com/google/gwt/user/cellview/client/CellTreeTest.java index 222888d..990abbf 100644 --- a/user/test/com/google/gwt/user/cellview/client/CellTreeTest.java +++ b/user/test/com/google/gwt/user/cellview/client/CellTreeTest.java
@@ -159,6 +159,33 @@ assertEquals("new", newNodeImpl.getCellParent().getInnerText()); } + public void testAriaSelectedAndExpanded() { + CellTree cellTree = (CellTree) tree; + TreeNode root = cellTree.getRootTreeNode(); + + TreeNode newNode = root.setChildOpen(1, true); + cellTree.rootNode.getChildNode(1).setSelected(true); + model.getRootDataProvider().refresh(); + model.getRootDataProvider().flush(); + root.setChildOpen(1, true); + CellTreeNodeView<?> newNodeImpl = cellTree.rootNode.getChildNode(1); + assertEquals("true", newNodeImpl.getElement().getAttribute("aria-selected")); + // Check aria-expanded on open + assertEquals("true", newNodeImpl.getElement().getAttribute("aria-expanded")); + + // Check aria-expanded on close + root.setChildOpen(1, false); + newNodeImpl = cellTree.rootNode.getChildNode(1); + assertEquals("false", newNodeImpl.getElement().getAttribute("aria-expanded")); + + cellTree.rootNode.getChildNode(1).setSelected(false); + model.getRootDataProvider().refresh(); + model.getRootDataProvider().flush(); + root.setChildOpen(1, true); + newNodeImpl = cellTree.rootNode.getChildNode(1); + assertEquals("false", newNodeImpl.getElement().getAttribute("aria-selected")); + } + public void testSetDefaultNodeSize() { CellTree cellTree = (CellTree) tree; TreeNode root = cellTree.getRootTreeNode(); @@ -175,6 +202,68 @@ assertEquals(5, d.getChildCount()); } + public void testAriaAttributes() { + CellTree cellTree = (CellTree) tree; + TreeNode rootNode = cellTree.getRootTreeNode(); + + // Open a child node. + TreeNode aNode = rootNode.setChildOpen(0, true); + + // Check role="tree" + assertEquals("tree", cellTree.rootNode.getElement().getAttribute("role")); + + // Check 1st level + CellTreeNodeView<?> aView = cellTree.rootNode.getChildNode(0); + CellTreeNodeView<?> bView = cellTree.rootNode.getChildNode(1); + + // Check treeitem role, level, posinset, and setsize + assertEquals("treeitem", aView.getElement().getAttribute("role")); + assertEquals("1", aView.getElement().getAttribute("aria-level")); + assertEquals("1", aView.getElement().getAttribute("aria-posinset")); + assertEquals("10", aView.getElement().getAttribute("aria-setsize")); + + assertEquals("treeitem", bView.getElement().getAttribute("role")); + assertEquals("1", bView.getElement().getAttribute("aria-level")); + assertEquals("2", bView.getElement().getAttribute("aria-posinset")); + assertEquals("10", bView.getElement().getAttribute("aria-setsize")); + + // Check 2nd level + assertTrue(aNode.getChildCount() != 0); + assertEquals(aNode, aView.getTreeNode()); + assertTrue(aView.getChildCount() != 0); + CellTreeNodeView<?> aViewChild = aView.getChildNode(0); + + // Check treeitem role, level, posinset, and setsize + assertEquals("treeitem", aViewChild.getElement().getAttribute("role")); + assertEquals("2", aViewChild.getElement().getAttribute("aria-level")); + assertEquals("1", aViewChild.getElement().getAttribute("aria-posinset")); + assertEquals("10", aViewChild.getElement().getAttribute("aria-setsize")); + + // Check aria-expanded + assertEquals("true", aView.getElement().getAttribute("aria-expanded")); + assertEquals("false", aViewChild.getElement().getAttribute("aria-expanded")); + while (!aNode.isChildLeaf(0)) { + aNode = aNode.setChildOpen(0, true); + aView = aView.getChildNode(0); + } + assertEquals(aNode, aView.getTreeNode()); + assertEquals("", aView.getChildNode(0).getElement().getAttribute("aria-expanded")); + + // Change default size and check aria-setsize and aria-posinset + cellTree.setDefaultNodeSize(5); + TreeNode cNode = rootNode.setChildOpen(3, true); + CellTreeNodeView<?> cView = cellTree.rootNode.getChildNode(3); + assertEquals(5, cNode.getChildCount()); + + CellTreeNodeView<?> cViewChildFirst = cView.getChildNode(0); + assertEquals("1", cViewChildFirst.getElement().getAttribute("aria-posinset")); + assertEquals("5", cViewChildFirst.getElement().getAttribute("aria-setsize")); + + CellTreeNodeView<?> cViewChildLast = cView.getChildNode(cView.getChildCount() - 1); + assertEquals("5", cViewChildLast.getElement().getAttribute("aria-posinset")); + assertEquals("5", cViewChildLast.getElement().getAttribute("aria-setsize")); + } + @Override protected <T> CellTree createAbstractCellTree(TreeViewModel model, T rootValue) { return new CellTree(model, rootValue);