import com.google.gwt.aria.client.Roles;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
+import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.event.dom.client.ScrollEvent;
import com.google.gwt.event.dom.client.ScrollHandler;
+import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.Event.NativePreviewEvent;
+import com.google.gwt.user.client.Event.NativePreviewHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ApplicationConnection;
private String assistivePrefix;
private String assistivePostfix;
+ private Element topTabStop;
+ private Element bottomTabStop;
+
+ private NativePreviewHandler topEventBlocker;
+ private NativePreviewHandler bottomEventBlocker;
+
+ private HandlerRegistration topBlockerRegistration;
+ private HandlerRegistration bottomBlockerRegistration;
+
+ // Prevents leaving the window with the Tab key when true
+ private boolean doTabStop;
+
/**
* If centered (via UIDL), the window should stay in the centered -mode
* until a position is received from the server, or the user moves or
* window is open.
*/
getApplicationConnection().getUIConnector().getWidget().storeFocus();
+
+ /*
+ * When this window gets reattached, set the tabstop to the previous
+ * state.
+ */
+ setTabStopEnabled(doTabStop);
}
@Override
*/
getApplicationConnection().getUIConnector().getWidget()
.focusStoredElement();
+
+ removeTabBlockHandlers();
+ }
+
+ private void removeTabBlockHandlers() {
+ if (topBlockerRegistration != null) {
+ topBlockerRegistration.removeHandler();
+ topBlockerRegistration = null;
+
+ bottomBlockerRegistration.removeHandler();
+ bottomBlockerRegistration = null;
+ }
}
public void bringToFront() {
protected void constructDOM() {
setStyleName(CLASSNAME);
+ topTabStop = DOM.createDiv();
+ DOM.setElementAttribute(topTabStop, "tabindex", "0");
+
header = DOM.createDiv();
DOM.setElementProperty(header, "className", CLASSNAME + "-outerheader");
headerText = DOM.createDiv();
DOM.setElementAttribute(closeBox, "tabindex", "0");
DOM.appendChild(footer, resizeBox);
+ bottomTabStop = DOM.createDiv();
+ DOM.setElementAttribute(bottomTabStop, "tabindex", "0");
+
wrapper = DOM.createDiv();
DOM.setElementProperty(wrapper, "className", CLASSNAME + "-wrap");
+ DOM.appendChild(wrapper, topTabStop);
DOM.appendChild(wrapper, header);
DOM.appendChild(wrapper, maximizeRestoreBox);
DOM.appendChild(wrapper, closeBox);
DOM.appendChild(header, headerText);
DOM.appendChild(wrapper, contents);
DOM.appendChild(wrapper, footer);
+ DOM.appendChild(wrapper, bottomTabStop);
DOM.appendChild(super.getContainerElement(), wrapper);
sinkEvents(Event.ONDBLCLICK | Event.MOUSEEVENTS | Event.TOUCHEVENTS
AriaHelper.ensureHasId(headerText);
Roles.getDialogRole().setAriaLabelledbyProperty(getElement(),
Id.of(headerText));
+
+ // Handlers to Prevent tab to leave the window
+ topEventBlocker = new NativePreviewHandler() {
+ @Override
+ public void onPreviewNativeEvent(NativePreviewEvent event) {
+ NativeEvent nativeEvent = event.getNativeEvent();
+ if (nativeEvent.getEventTarget().cast() == topTabStop
+ && nativeEvent.getKeyCode() == KeyCodes.KEY_TAB
+ && nativeEvent.getShiftKey()) {
+ nativeEvent.preventDefault();
+ }
+ }
+ };
+
+ bottomEventBlocker = new NativePreviewHandler() {
+ @Override
+ public void onPreviewNativeEvent(NativePreviewEvent event) {
+ NativeEvent nativeEvent = event.getNativeEvent();
+ if (nativeEvent.getEventTarget().cast() == bottomTabStop
+ && nativeEvent.getKeyCode() == KeyCodes.KEY_TAB
+ && !nativeEvent.getShiftKey()) {
+ nativeEvent.preventDefault();
+ }
+ }
+ };
+ }
+
+ /**
+ * Sets the message that is provided to users of assistive devices when the
+ * user reaches the top of the window when leaving a window with the tab key
+ * is prevented.
+ * <p>
+ * This message is not visible on the screen.
+ *
+ * @param topMessage
+ * String provided when the user navigates with Shift-Tab keys to
+ * the top of the window
+ */
+ public void setTabStopTopAssistiveText(String topMessage) {
+ Roles.getNoteRole().setAriaLabelProperty(topTabStop, topMessage);
+ }
+
+ /**
+ * Sets the message that is provided to users of assistive devices when the
+ * user reaches the bottom of the window when leaving a window with the tab
+ * key is prevented.
+ * <p>
+ * This message is not visible on the screen.
+ *
+ * @param bottomMessage
+ * String provided when the user navigates with the Tab key to
+ * the bottom of the window
+ */
+ public void setTabStopBottomAssistiveText(String bottomMessage) {
+ Roles.getNoteRole().setAriaLabelProperty(bottomTabStop, bottomMessage);
+ }
+
+ /**
+ * Gets the message that is provided to users of assistive devices when the
+ * user reaches the top of the window when leaving a window with the tab key
+ * is prevented.
+ *
+ * @return the top message
+ */
+ public String getTabStopTopAssistiveText() {
+ return Roles.getNoteRole().getAriaLabelProperty(topTabStop);
+ }
+
+ /**
+ * Gets the message that is provided to users of assistive devices when the
+ * user reaches the bottom of the window when leaving a window with the tab
+ * key is prevented.
+ *
+ * @return the bottom message
+ */
+ public String getTabStopBottomAssistiveText() {
+ return Roles.getNoteRole().getAriaLabelProperty(bottomTabStop);
}
/**
Roles.getDialogRole().set(getElement());
}
}
+
+ /**
+ * Registers the handlers that prevent to leave the window using the
+ * Tab-key.
+ *
+ * @param doTabStop
+ * true to prevent leaving the window, false to allow leaving the
+ * window
+ */
+ public void setTabStopEnabled(boolean doTabStop) {
+ this.doTabStop = doTabStop;
+
+ if (doTabStop) {
+ if (topBlockerRegistration == null) {
+ topBlockerRegistration = Event
+ .addNativePreviewHandler(topEventBlocker);
+ bottomBlockerRegistration = Event
+ .addNativePreviewHandler(bottomEventBlocker);
+ }
+ } else {
+ removeTabBlockHandlers();
+ }
+ }
}
public WindowRole getAssistiveRole() {
return getState().role;
}
+
+ /**
+ * Set if it should be prevented to set the focus to a component outside the
+ * window with the tab key.
+ * <p>
+ * This is meant to help users of assistive devices to not leaving the
+ * window unintentionally.
+ *
+ * @param tabStop
+ * true to keep the focus inside the window when reaching the top
+ * or bottom, false (default) to allow leaving the window
+ */
+ public void setTabStopEnabled(boolean tabStop) {
+ getState().assistiveTabStop = tabStop;
+ }
+
+ /**
+ * Get if it is prevented to leave a window with the tab key.
+ *
+ * @return true when the focus is limited to inside the window, false when
+ * focus can leave the window
+ */
+ public boolean isTabStopEnabled() {
+ return getState().assistiveTabStop;
+ }
+
+ /**
+ * Sets the message that is provided to users of assistive devices when the
+ * user reaches the top of the window when leaving a window with the tab key
+ * is prevented.
+ * <p>
+ * This message is not visible on the screen.
+ *
+ * @param topMessage
+ * String provided when the user navigates with Shift-Tab keys to
+ * the top of the window
+ */
+ public void setTabStopTopAssistiveText(String topMessage) {
+ getState().assistiveTabStopTopText = topMessage;
+ }
+
+ /**
+ * Sets the message that is provided to users of assistive devices when the
+ * user reaches the bottom of the window when leaving a window with the tab
+ * key is prevented.
+ * <p>
+ * This message is not visible on the screen.
+ *
+ * @param bottomMessage
+ * String provided when the user navigates with the Tab key to
+ * the bottom of the window
+ */
+ public void setTabStopBottomAssistiveText(String bottomMessage) {
+ getState().assistiveTabStopBottomText = bottomMessage;
+ }
+
+ /**
+ * Gets the message that is provided to users of assistive devices when the
+ * user reaches the top of the window when leaving a window with the tab key
+ * is prevented.
+ *
+ * @return the top message
+ */
+ public String getTabStopTopAssistiveText() {
+ return getState().assistiveTabStopTopText;
+ }
+
+ /**
+ * Gets the message that is provided to users of assistive devices when the
+ * user reaches the bottom of the window when leaving a window with the tab
+ * key is prevented.
+ *
+ * @return the bottom message
+ */
+ public String getTabStopBottomAssistiveText() {
+ return getState().assistiveTabStopBottomText;
+ }
}
package com.vaadin.tests.components.window;
import com.vaadin.server.ThemeResource;
+import com.vaadin.server.VaadinRequest;
import com.vaadin.shared.ui.window.WindowState.WindowRole;
-import com.vaadin.tests.components.TestBase;
+import com.vaadin.tests.components.AbstractTestUI;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.CheckBox;
import com.vaadin.ui.TextField;
import com.vaadin.ui.Window;
-public class ExtraWindowShownWaiAria extends TestBase {
+public class ExtraWindowShownWaiAria extends AbstractTestUI {
@Override
- protected void setup() {
+ protected void setup(VaadinRequest request) {
final CheckBox modal = new CheckBox("Modal dialog");
+ modal.setTabIndex(7);
final CheckBox additionalDescription = new CheckBox(
"Additional Description");
+ final CheckBox tabStop = new CheckBox(
+ "Prevent leaving window with Tab key");
+ final CheckBox tabOrder = new CheckBox("Change Taborder");
final TextField prefix = new TextField("Prefix: ");
final TextField postfix = new TextField("Postfix: ");
+ final TextField topTabStopMessage = new TextField(
+ "Top Tab Stop Message");
+ final TextField bottomTabStopMessage = new TextField(
+ "Bottom Tab Stop Message");
+
Button simple = new Button("Open Alert Dialog",
new Button.ClickListener() {
description2);
}
- layout.addComponent(new Button("Close",
+ w.setTabStopEnabled(tabStop.getValue());
+ w.setTabStopTopAssistiveText(topTabStopMessage
+ .getValue());
+ w.setTabStopBottomAssistiveText(bottomTabStopMessage
+ .getValue());
+
+ Button close = new Button("Close",
new Button.ClickListener() {
@Override
public void buttonClick(ClickEvent event) {
w.close();
}
- }));
+ });
+ layout.addComponent(close);
Button iconButton = new Button("A button with icon");
iconButton.setIcon(new ThemeResource(
"../runo/icons/16/ok.png"));
event.getButton().getUI().addWindow(w);
iconButton.focus();
+
+ if (tabOrder.getValue()) {
+ close.setTabIndex(5);
+ }
}
});
description2);
}
+ w.setTabStopEnabled(tabStop.getValue());
+ w.setTabStopTopAssistiveText(topTabStopMessage
+ .getValue());
+ w.setTabStopBottomAssistiveText(bottomTabStopMessage
+ .getValue());
+
TextField name = new TextField("Name:");
form.addComponent(name);
event.getButton().getUI().addWindow(w);
name.focus();
+
+ if (tabOrder.getValue()) {
+ name.setTabIndex(5);
+ }
}
});
getLayout().addComponent(complex);
getLayout().addComponent(modal);
getLayout().addComponent(additionalDescription);
+ getLayout().addComponent(tabStop);
+ getLayout().addComponent(tabOrder);
getLayout().addComponent(prefix);
getLayout().addComponent(postfix);
+ getLayout().addComponent(topTabStopMessage);
+ getLayout().addComponent(bottomTabStopMessage);
+
}
@Override
- protected String getDescription() {
- // TODO Auto-generated method stub
- return null;
+ protected String getTestDescription() {
+ return "Test for WAI-ARIA implementation";
}
@Override
protected Integer getTicketNumber() {
- // TODO Auto-generated method stub
- return null;
+ return 11821;
}
-
}