diff options
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; + } +} |