aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--WebContent/VAADIN/themes/base/tabsheet/tabsheet.scss6
-rw-r--r--client/src/com/vaadin/client/VTooltip.java11
-rw-r--r--client/src/com/vaadin/client/ui/VTabsheet.java171
-rw-r--r--client/src/com/vaadin/client/ui/VTabsheetBase.java2
-rw-r--r--server/src/com/vaadin/ui/TabSheet.java33
-rw-r--r--uitest/src/com/vaadin/tests/components/tabsheet/TabKeyboardNavigation.html52
-rw-r--r--uitest/src/com/vaadin/tests/components/tabsheet/TabKeyboardNavigationWaiAria.java86
7 files changed, 340 insertions, 21 deletions
diff --git a/WebContent/VAADIN/themes/base/tabsheet/tabsheet.scss b/WebContent/VAADIN/themes/base/tabsheet/tabsheet.scss
index 1a799814c1..14def56ab5 100644
--- a/WebContent/VAADIN/themes/base/tabsheet/tabsheet.scss
+++ b/WebContent/VAADIN/themes/base/tabsheet/tabsheet.scss
@@ -106,6 +106,12 @@
.#{$primaryStyleName}-tabitem-selected .v-caption {
cursor: default;
}
+.#{$primaryStyleName}-tabitem-focus .v-captiontext {
+ text-decoration: underline;
+}
+.#{$primaryStyleName}-tabitem-selected.#{$primaryStyleName}-tabitem-focus .v-captiontext {
+ text-decoration: inherit;
+}
.#{$primaryStyleName}-content {
border: 1px solid #aaa;
/* Vertical borders are not supported, use v-tabsheet-tabcontainer and v-tabsheet-deco to present these borders */
diff --git a/client/src/com/vaadin/client/VTooltip.java b/client/src/com/vaadin/client/VTooltip.java
index 6191821988..57e72aedfc 100644
--- a/client/src/com/vaadin/client/VTooltip.java
+++ b/client/src/com/vaadin/client/VTooltip.java
@@ -94,6 +94,17 @@ public class VTooltip extends VOverlay {
}
/**
+ * Show the tooltip with the provided info for assistive devices.
+ *
+ * @param info
+ * with the content of the tooltip
+ */
+ public void showAssistive(TooltipInfo info) {
+ updatePosition(null, true);
+ show(info);
+ }
+
+ /**
* Show a popup containing the information in the "info" tooltip
*
* @param info
diff --git a/client/src/com/vaadin/client/ui/VTabsheet.java b/client/src/com/vaadin/client/ui/VTabsheet.java
index 10fa0dff16..729a8c5123 100644
--- a/client/src/com/vaadin/client/ui/VTabsheet.java
+++ b/client/src/com/vaadin/client/ui/VTabsheet.java
@@ -19,6 +19,9 @@ package com.vaadin.client.ui;
import java.util.Iterator;
import java.util.List;
+import com.google.gwt.aria.client.Id;
+import com.google.gwt.aria.client.Roles;
+import com.google.gwt.aria.client.SelectedValue;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.dom.client.DivElement;
import com.google.gwt.dom.client.Style;
@@ -55,6 +58,7 @@ import com.vaadin.client.TooltipInfo;
import com.vaadin.client.UIDL;
import com.vaadin.client.Util;
import com.vaadin.client.VCaption;
+import com.vaadin.client.ui.aria.AriaHelper;
import com.vaadin.shared.AbstractComponentState;
import com.vaadin.shared.EventId;
import com.vaadin.shared.ui.ComponentStateUtil;
@@ -94,12 +98,18 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
+ "-selected";
private static final String TD_SELECTED_FIRST_CLASSNAME = TD_SELECTED_CLASSNAME
+ "-first";
+ private static final String TD_FOCUS_CLASSNAME = TD_CLASSNAME
+ + "-focus";
+ private static final String TD_FOCUS_FIRST_CLASSNAME = TD_FOCUS_CLASSNAME
+ + "-first";
private static final String TD_DISABLED_CLASSNAME = TD_CLASSNAME
+ "-disabled";
private static final String DIV_CLASSNAME = CLASSNAME + "-tabitem";
private static final String DIV_SELECTED_CLASSNAME = DIV_CLASSNAME
+ "-selected";
+ private static final String DIV_FOCUS_CLASSNAME = DIV_CLASSNAME
+ + "-focus";
private TabCaption tabCaption;
Element td = getElement();
@@ -119,6 +129,10 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
this.tabBar = tabBar;
setStyleName(td, TD_CLASSNAME);
+ Roles.getTabRole().set(getElement());
+ Roles.getTabRole().setAriaSelectedState(getElement(),
+ SelectedValue.FALSE);
+
div = DOM.createDiv();
focusImpl.setTabIndex(td, -1);
setStyleName(div, DIV_CLASSNAME);
@@ -129,6 +143,9 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
.getApplicationConnection());
add(tabCaption);
+ Roles.getTabRole().setAriaLabelledbyProperty(getElement(),
+ Id.of(tabCaption.getElement()));
+
addFocusHandler(getTabsheet());
addBlurHandler(getTabsheet());
addKeyDownHandler(getTabsheet());
@@ -140,6 +157,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
public void setHiddenOnServer(boolean hiddenOnServer) {
this.hiddenOnServer = hiddenOnServer;
+ Roles.getTabRole().setAriaHiddenState(getElement(), hiddenOnServer);
}
@Override
@@ -154,6 +172,8 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
public void setEnabledOnServer(boolean enabled) {
enabledOnServer = enabled;
+ Roles.getTabRole().setAriaDisabledState(getElement(), !enabled);
+
setStyleName(td, TD_DISABLED_CLASSNAME, !enabled);
if (!enabled) {
focusImpl.setTabIndex(td, -1);
@@ -177,10 +197,18 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
* true if the Tab is the first visible Tab
*/
public void setStyleNames(boolean selected, boolean first) {
+ setStyleNames(selected, first, false);
+ }
+
+ public void setStyleNames(boolean selected, boolean first,
+ boolean keyboardFocus) {
setStyleName(td, TD_FIRST_CLASSNAME, first);
setStyleName(td, TD_SELECTED_CLASSNAME, selected);
setStyleName(td, TD_SELECTED_FIRST_CLASSNAME, selected && first);
setStyleName(div, DIV_SELECTED_CLASSNAME, selected);
+ setStyleName(td, TD_FOCUS_CLASSNAME, keyboardFocus);
+ setStyleName(td, TD_FOCUS_FIRST_CLASSNAME, keyboardFocus && first);
+ setStyleName(div, DIV_FOCUS_CLASSNAME, keyboardFocus);
}
public void setTabulatorIndex(int tabIndex) {
@@ -260,6 +288,23 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
public void blur() {
focusImpl.blur(td);
}
+
+ public boolean hasTooltip() {
+ return tabCaption.getTooltipInfo() != null;
+ }
+
+ public TooltipInfo getTooltipInfo() {
+ return tabCaption.getTooltipInfo();
+ }
+
+ public void setAssistiveDescription(String descriptionId) {
+ Roles.getTablistRole().setAriaDescribedbyProperty(getElement(),
+ Id.of(descriptionId));
+ }
+
+ public void removeAssistiveDescription() {
+ Roles.getTablistRole().removeAriaDescribedbyProperty(getElement());
+ }
}
public static class TabCaption extends VCaption {
@@ -273,6 +318,8 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
super(client);
this.client = client;
this.tab = tab;
+
+ AriaHelper.ensureHasId(getElement());
}
public boolean updateCaption(UIDL uidl) {
@@ -330,6 +377,10 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
closeButton.setInnerHTML("×");
closeButton
.setClassName(VTabsheet.CLASSNAME + "-caption-close");
+
+ Roles.getTabRole().setAriaHiddenState(closeButton, true);
+ Roles.getTabRole().setAriaDisabledState(closeButton, true);
+
getElement().appendChild(closeButton);
} else if (!closable && closeButton != null) {
getElement().removeChild(closeButton);
@@ -376,6 +427,8 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
this.tabsheet = tabsheet;
Element el = DOM.createTable();
+ Roles.getPresentationRole().set(el);
+
Element tbody = DOM.createTBody();
DOM.appendChild(el, tbody);
DOM.appendChild(tbody, tr);
@@ -432,11 +485,9 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
}
int index = getWidgetIndex(caption.getParent());
- // IE needs explicit focus()
- if (BrowserInfo.get().isIE()) {
- getTabsheet().focus();
- }
- getTabsheet().onTabSelected(index);
+
+ getTabsheet().focus();
+ getTabsheet().loadTabSheet(index);
}
public VTabsheet getTabsheet() {
@@ -454,13 +505,18 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
final Tab newSelected = getTab(index);
final Tab oldSelected = selected;
- newSelected.setStyleNames(true, isFirstVisibleTab(index));
+ newSelected.setStyleNames(true, isFirstVisibleTab(index), true);
newSelected.setTabulatorIndex(getTabsheet().tabulatorIndex);
+ Roles.getTabRole().setAriaSelectedState(newSelected.getElement(),
+ SelectedValue.TRUE);
if (oldSelected != null && oldSelected != newSelected) {
oldSelected.setStyleNames(false,
isFirstVisibleTab(getWidgetIndex(oldSelected)));
oldSelected.setTabulatorIndex(-1);
+
+ Roles.getTabRole().setAriaSelectedState(
+ oldSelected.getElement(), SelectedValue.FALSE);
}
// Update the field holding the currently selected tab
@@ -471,6 +527,23 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
getTab(tabsheet.activeTabIndex).recalculateCaptionWidth();
}
+ public void navigateTab(int fromIndex, int toIndex) {
+ Tab newNavigated = getTab(toIndex);
+ if (newNavigated == null) {
+ throw new IllegalArgumentException(
+ "Tab at provided index toIndex was not found");
+ }
+
+ Tab oldNavigated = getTab(fromIndex);
+ newNavigated.setStyleNames(newNavigated.equals(selected),
+ isFirstVisibleTab(toIndex), true);
+
+ if (oldNavigated != null && fromIndex != toIndex) {
+ oldNavigated.setStyleNames(oldNavigated.equals(selected),
+ isFirstVisibleTab(fromIndex), false);
+ }
+ }
+
public void removeTab(int i) {
Tab tab = getTab(i);
if (tab == null) {
@@ -603,7 +676,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
/**
* @return Whether the tab could be selected or not.
*/
- private boolean onTabSelected(final int tabIndex) {
+ private boolean canSelectTab(final int tabIndex) {
Tab tab = tb.getTab(tabIndex);
if (client == null || disabled || waitingForResponse) {
return false;
@@ -611,15 +684,32 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
if (!tab.isEnabledOnServer() || tab.isHiddenOnServer()) {
return false;
}
- if (activeTabIndex != tabIndex) {
+
+ // Note that we return true when tabIndex == activeTabIndex; the active
+ // tab could be selected, it's just a no-op.
+ return true;
+ }
+
+ /**
+ * Load the content of a tab of the provided index.
+ *
+ * @param index
+ * of the tab to load
+ */
+ public void loadTabSheet(int tabIndex) {
+ if (activeTabIndex != tabIndex && canSelectTab(tabIndex)) {
tb.selectTab(tabIndex);
// If this TabSheet already has focus, set the new selected tab
// as focused.
if (focusedTab != null) {
- focusedTab = tab;
+ focusedTab = tb.getTab(tabIndex);
+ focusedTab.focus();
}
+ activeTabIndex = tabIndex;
+ focusedTabIndex = tabIndex;
+
addStyleDependentName("loading");
// Hide the current contents so a loading indicator can be shown
// instead
@@ -631,9 +721,6 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
.toString(), true);
waitingForResponse = true;
}
- // Note that we return true when tabIndex == activeTabIndex; the active
- // tab could be selected, it's just a no-op.
- return true;
}
public ApplicationConnection getApplicationConnection() {
@@ -675,22 +762,29 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
DOM.setStyleAttribute(getElement(), "overflow", "hidden");
tabs = DOM.createDiv();
DOM.setElementProperty(tabs, "className", TABS_CLASSNAME);
+ Roles.getTablistRole().set(tabs);
scroller = DOM.createDiv();
+ Roles.getTablistRole().setAriaHiddenState(scroller, true);
DOM.setElementProperty(scroller, "className", SCROLLER_CLASSNAME);
scrollerPrev = DOM.createButton();
+ scrollerPrev.setTabIndex(-1);
DOM.setElementProperty(scrollerPrev, "className", SCROLLER_CLASSNAME
+ "Prev");
+ Roles.getTablistRole().setAriaHiddenState(scrollerPrev, true);
DOM.sinkEvents(scrollerPrev, Event.ONCLICK);
scrollerNext = DOM.createButton();
+ scrollerNext.setTabIndex(-1);
DOM.setElementProperty(scrollerNext, "className", SCROLLER_CLASSNAME
+ "Next");
+ Roles.getTablistRole().setAriaHiddenState(scrollerNext, true);
DOM.sinkEvents(scrollerNext, Event.ONCLICK);
DOM.appendChild(getElement(), tabs);
// Tabs
tp.setStyleName(CLASSNAME + "-tabsheetpanel");
contentNode = DOM.createDiv();
+ Roles.getTabpanelRole().set(contentNode);
deco = DOM.createDiv();
@@ -1095,7 +1189,10 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
@Override
public void onBlur(BlurEvent event) {
+ getApplicationConnection().getVTooltip().hideTooltip();
+
if (focusedTab != null && event.getSource() instanceof Tab) {
+ focusedTab.removeAssistiveDescription();
focusedTab = null;
if (client.hasEventListeners(this, EventId.BLUR)) {
client.updateVariable(id, EventId.BLUR, "", true);
@@ -1110,6 +1207,13 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
if (client.hasEventListeners(this, EventId.FOCUS)) {
client.updateVariable(id, EventId.FOCUS, "", true);
}
+
+ if (focusedTab.hasTooltip()) {
+ focusedTab.setAssistiveDescription(getApplicationConnection()
+ .getVTooltip().getUniqueId());
+ getApplicationConnection().getVTooltip().showAssistive(
+ focusedTab.getTooltipInfo());
+ }
}
}
@@ -1129,13 +1233,17 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
if (keycode == getPreviousTabKey()) {
selectPreviousTab();
+ event.stopPropagation();
} else if (keycode == getNextTabKey()) {
selectNextTab();
+ event.stopPropagation();
} else if (keycode == getCloseTabKey()) {
Tab tab = tb.getTab(activeTabIndex);
if (tab.isClosable()) {
tab.onClose();
}
+ } else if (keycode == getSelectTabKey()) {
+ loadTabSheet(focusedTabIndex);
}
}
}
@@ -1148,6 +1256,10 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
return KeyCodes.KEY_LEFT;
}
+ protected int getSelectTabKey() {
+ return 32; // Space key
+ }
+
/**
* @return The key code of the keyboard shortcut that selects the next tab
* in a focused tabsheet.
@@ -1165,18 +1277,27 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
}
private void selectPreviousTab() {
- int newTabIndex = activeTabIndex;
+ int newTabIndex = focusedTabIndex;
// Find the previous visible and enabled tab if any.
do {
newTabIndex--;
- } while (newTabIndex >= 0 && !onTabSelected(newTabIndex));
+ } while (newTabIndex >= 0 && !canSelectTab(newTabIndex));
if (newTabIndex >= 0) {
- activeTabIndex = newTabIndex;
+ tb.navigateTab(focusedTabIndex, newTabIndex);
+ focusedTabIndex = newTabIndex;
+
+ // If this TabSheet already has focus, set the new selected tab
+ // as focused.
+ if (focusedTab != null) {
+ focusedTab = tb.getTab(focusedTabIndex);
+ focusedTab.focus();
+ }
+
if (isScrolledTabs()) {
// Scroll until the new active tab is visible
int newScrollerIndex = scrollerIndex;
- while (tb.getTab(activeTabIndex).getAbsoluteLeft() < getAbsoluteLeft()
+ while (tb.getTab(focusedTabIndex).getAbsoluteLeft() < getAbsoluteLeft()
&& newScrollerIndex != -1) {
newScrollerIndex = tb.scrollLeft(newScrollerIndex);
}
@@ -1187,18 +1308,28 @@ public class VTabsheet extends VTabsheetBase implements Focusable,
}
private void selectNextTab() {
- int newTabIndex = activeTabIndex;
+ int newTabIndex = focusedTabIndex;
// Find the next visible and enabled tab if any.
do {
newTabIndex++;
- } while (newTabIndex < getTabCount() && !onTabSelected(newTabIndex));
+ } while (newTabIndex < getTabCount() && !canSelectTab(newTabIndex));
if (newTabIndex < getTabCount()) {
- activeTabIndex = newTabIndex;
+
+ tb.navigateTab(focusedTabIndex, newTabIndex);
+ focusedTabIndex = newTabIndex;
+
+ // If this TabSheet already has focus, set the new selected tab
+ // as focused.
+ if (focusedTab != null) {
+ focusedTab = tb.getTab(focusedTabIndex);
+ focusedTab.focus();
+ }
+
if (isClippedTabs()) {
// Scroll until the new active tab is completely visible
int newScrollerIndex = scrollerIndex;
- while (isClipped(tb.getTab(activeTabIndex))
+ while (isClipped(tb.getTab(focusedTabIndex))
&& newScrollerIndex != -1) {
newScrollerIndex = tb.scrollRight(newScrollerIndex);
}
diff --git a/client/src/com/vaadin/client/ui/VTabsheetBase.java b/client/src/com/vaadin/client/ui/VTabsheetBase.java
index 0923248115..bcd8811c7d 100644
--- a/client/src/com/vaadin/client/ui/VTabsheetBase.java
+++ b/client/src/com/vaadin/client/ui/VTabsheetBase.java
@@ -42,6 +42,8 @@ public abstract class VTabsheetBase extends ComplexPanel {
/** For internal use only. May be removed or replaced in the future. */
public int activeTabIndex = 0;
/** For internal use only. May be removed or replaced in the future. */
+ public int focusedTabIndex = 0;
+ /** For internal use only. May be removed or replaced in the future. */
public boolean disabled;
/** For internal use only. May be removed or replaced in the future. */
public boolean readonly;
diff --git a/server/src/com/vaadin/ui/TabSheet.java b/server/src/com/vaadin/ui/TabSheet.java
index 3ba1861b6f..a1f9e9dd26 100644
--- a/server/src/com/vaadin/ui/TabSheet.java
+++ b/server/src/com/vaadin/ui/TabSheet.java
@@ -615,6 +615,11 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
// connector
if (selected != null) {
selected.markAsDirtyRecursive();
+
+ Tab tab = getTab(c);
+ if (tab != null && tab.getDefaultFocusComponent() != null) {
+ tab.getDefaultFocusComponent().focus();
+ }
}
}
@@ -955,6 +960,23 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
public void setClosable(boolean closable);
/**
+ * Set the component that should automatically focused when the tab is
+ * selected.
+ *
+ * @param component
+ * the component to focus
+ */
+ public void setDefaultFocusComponent(Focusable component);
+
+ /**
+ * Get the component that should be automatically focused when the tab
+ * is selected.
+ *
+ * @return the focusable component
+ */
+ public Focusable getDefaultFocusComponent();
+
+ /**
* Returns the enabled status for the tab. A disabled tab is shown as
* such in the tab bar and cannot be selected.
*
@@ -1136,6 +1158,7 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
private String styleName;
private String id;
private String iconAltText = "";
+ private Focusable defaultFocus;
public TabSheetTabImpl(String caption, Resource icon) {
if (caption == null) {
@@ -1189,6 +1212,16 @@ public class TabSheet extends AbstractComponentContainer implements Focusable,
}
@Override
+ public void setDefaultFocusComponent(Focusable defaultFocus) {
+ this.defaultFocus = defaultFocus;
+ }
+
+ @Override
+ public Focusable getDefaultFocusComponent() {
+ return defaultFocus;
+ }
+
+ @Override
public boolean isEnabled() {
return enabled;
}
diff --git a/uitest/src/com/vaadin/tests/components/tabsheet/TabKeyboardNavigation.html b/uitest/src/com/vaadin/tests/components/tabsheet/TabKeyboardNavigation.html
index 129061e69c..acf66fb27f 100644
--- a/uitest/src/com/vaadin/tests/components/tabsheet/TabKeyboardNavigation.html
+++ b/uitest/src/com/vaadin/tests/components/tabsheet/TabKeyboardNavigation.html
@@ -3,7 +3,7 @@
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head profile="http://selenium-ide.openqa.org/profiles/test-case">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
-<link rel="selenium.base" href="http://localhost:8068/" />
+<link rel="selenium.base" href="http://localhost:8080/" />
<title>TabKeyboardNavigation</title>
</head>
<body>
@@ -29,6 +29,16 @@
<tr>
<td>assertText</td>
<td>vaadin=runTabKeyboardNavigation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VTabsheet[0]/VTabsheetPanel[0]/VVerticalLayout[1]/ChildComponentContainer[0]/VLabel[0]</td>
+ <td>Tab 1</td>
+</tr>
+<tr>
+ <td>pressSpecialKey</td>
+ <td>vaadin=runTabKeyboardNavigation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VTabsheet[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td>
+ <td>enter</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runTabKeyboardNavigation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VTabsheet[0]/VTabsheetPanel[0]/VVerticalLayout[1]/ChildComponentContainer[0]/VLabel[0]</td>
<td>Tab 2</td>
</tr>
<tr>
@@ -49,6 +59,16 @@
<tr>
<td>assertText</td>
<td>vaadin=runTabKeyboardNavigation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VTabsheet[0]/VTabsheetPanel[0]/VVerticalLayout[3]/ChildComponentContainer[0]/VLabel[0]</td>
+ <td>Tab 2</td>
+</tr>
+<tr>
+ <td>pressSpecialKey</td>
+ <td>vaadin=runTabKeyboardNavigation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VTabsheet[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[2]</td>
+ <td>enter</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runTabKeyboardNavigation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VTabsheet[0]/VTabsheetPanel[0]/VVerticalLayout[3]/ChildComponentContainer[0]/VLabel[0]</td>
<td>Tab 5</td>
</tr>
<tr>
@@ -69,6 +89,16 @@
<tr>
<td>assertText</td>
<td>vaadin=runTabKeyboardNavigation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VTabsheet[0]/VTabsheetPanel[0]/VVerticalLayout[4]/ChildComponentContainer[0]/VLabel[0]</td>
+ <td>Tab 5</td>
+</tr>
+<tr>
+ <td>pressSpecialKey</td>
+ <td>vaadin=runTabKeyboardNavigation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VTabsheet[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td>
+ <td>enter</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runTabKeyboardNavigation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VTabsheet[0]/VTabsheetPanel[0]/VVerticalLayout[4]/ChildComponentContainer[0]/VLabel[0]</td>
<td>Tab 6</td>
</tr>
<tr>
@@ -124,6 +154,16 @@
<tr>
<td>assertText</td>
<td>vaadin=runTabKeyboardNavigation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VTabsheet[0]/VTabsheetPanel[0]/VVerticalLayout[8]/ChildComponentContainer[0]/VLabel[0]</td>
+ <td>Tab 9</td>
+</tr>
+<tr>
+ <td>pressSpecialKey</td>
+ <td>vaadin=runTabKeyboardNavigation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VTabsheet[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[10]</td>
+ <td>enter</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runTabKeyboardNavigation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VTabsheet[0]/VTabsheetPanel[0]/VVerticalLayout[8]/ChildComponentContainer[0]/VLabel[0]</td>
<td>Tab 12</td>
</tr>
<tr>
@@ -154,6 +194,16 @@
<tr>
<td>assertText</td>
<td>vaadin=runTabKeyboardNavigation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VTabsheet[0]/VTabsheetPanel[0]/VVerticalLayout[0]/ChildComponentContainer[0]/VLabel[0]</td>
+ <td>Tab 5</td>
+</tr>
+<tr>
+ <td>pressSpecialKey</td>
+ <td>vaadin=runTabKeyboardNavigation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VTabsheet[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[1]</td>
+ <td>enter</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runTabKeyboardNavigation::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[4]/VTabsheet[0]/VTabsheetPanel[0]/VVerticalLayout[0]/ChildComponentContainer[0]/VLabel[0]</td>
<td>Tab 1</td>
</tr>
<tr>
diff --git a/uitest/src/com/vaadin/tests/components/tabsheet/TabKeyboardNavigationWaiAria.java b/uitest/src/com/vaadin/tests/components/tabsheet/TabKeyboardNavigationWaiAria.java
new file mode 100644
index 0000000000..e394594176
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/tabsheet/TabKeyboardNavigationWaiAria.java
@@ -0,0 +1,86 @@
+package com.vaadin.tests.components.tabsheet;
+
+import java.util.ArrayList;
+
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.tests.components.AbstractTestUI;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.Button.ClickListener;
+import com.vaadin.ui.Component;
+import com.vaadin.ui.Layout;
+import com.vaadin.ui.TabSheet;
+import com.vaadin.ui.TabSheet.Tab;
+import com.vaadin.ui.TextField;
+import com.vaadin.ui.VerticalLayout;
+
+public class TabKeyboardNavigationWaiAria extends AbstractTestUI {
+
+ int index = 1;
+ ArrayList<Component> tabs = new ArrayList<Component>();
+ TabSheet ts = new TabSheet();
+
+ @Override
+ protected void setup(VaadinRequest request) {
+ ts.setWidth("500px");
+ ts.setHeight("500px");
+
+ for (int i = 0; i < 5; ++i) {
+ addTab();
+ }
+
+ Button addTab = new Button("Add a tab", new ClickListener() {
+ @Override
+ public void buttonClick(ClickEvent event) {
+ addTab();
+ }
+ });
+ Button focus = new Button("Focus tabsheet", new ClickListener() {
+ @Override
+ public void buttonClick(ClickEvent event) {
+ ts.focus();
+ }
+ });
+
+ addComponent(addTab);
+ addComponent(focus);
+
+ addComponent(ts);
+ }
+
+ @Override
+ protected String getTestDescription() {
+ return "The tab bar should be focusable and arrow keys should change focus for tabs. Space key selects a focused tab. The del key should close a tab if closable.";
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ return 11827;
+ }
+
+ private Tab addTab() {
+ Layout content = new VerticalLayout();
+ tabs.add(content);
+
+ TextField field = new TextField("Tab " + index + " label");
+ content.addComponent(field);
+
+ Tab tab = ts.addTab(content, "Tab " + index, null);
+
+ if (index == 2) {
+ tab.setClosable(true);
+ tab.setDescription("Tab 2 Tooltip");
+ }
+
+ if (index == 4) {
+ tab.setEnabled(false);
+ }
+
+ if (index == 5) {
+ tab.setDefaultFocusComponent(field);
+ }
+
+ index++;
+ return tab;
+ }
+}