Browse Source

Use Enter and Space keys to fire Window header buttons (#11517) (#11534)

* Fixes 11517. Use ENTER and SPACE keys as shortcuts for header buttons

* Merge branch 'master' into fix-11517

* Increase sleep time for test failing in validation

* Merge branch 'master' into fix-11517
tags/8.8.0.beta1
KatriHaapalinna 5 years ago
parent
commit
262863dbf6

+ 11
- 4
client/src/main/java/com/vaadin/client/ui/VWindow.java View File

@@ -434,7 +434,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner,
DOM.appendChild(super.getContainerElement(), wrapper);

sinkEvents(Event.ONDBLCLICK | Event.MOUSEEVENTS | Event.TOUCHEVENTS
| Event.ONCLICK | Event.ONLOSECAPTURE);
| Event.ONCLICK | Event.ONLOSECAPTURE | Event.ONKEYUP);

setWidget(contentPanel);

@@ -1008,18 +1008,21 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner,
final int type = event.getTypeInt();

final Element target = DOM.eventGetTarget(event);

if (resizing || resizeBox == target) {
onResizeEvent(event);
bubble = false;
// if clicked or key ENTER or SPACE is pressed
} else if (isClosable() && target == closeBox) {
if (type == Event.ONCLICK) {
if (type == Event.ONCLICK || (type == Event.ONKEYUP
&& isKeyEnterOrSpace(event.getKeyCode()))) {
onCloseClick();
}
bubble = false;
} else if (target == maximizeRestoreBox) {
// handled in connector
if (type != Event.ONCLICK) {
// if clicked or key ENTER or SPACE is pressed
if (type != Event.ONCLICK && !(type == Event.ONKEYUP
&& isKeyEnterOrSpace(event.getKeyCode()))) {
bubble = false;
}
} else if (header.isOrHasChild(target) && !dragging) {
@@ -1571,4 +1574,8 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner,
return Document.get().getBody()
.hasClassName(MODAL_WINDOW_OPEN_CLASSNAME);
}

private boolean isKeyEnterOrSpace(int keyCode) {
return keyCode == KeyCodes.KEY_ENTER || keyCode == KeyCodes.KEY_SPACE;
}
}

+ 21
- 1
client/src/main/java/com/vaadin/client/ui/window/WindowConnector.java View File

@@ -29,6 +29,9 @@ import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.DoubleClickEvent;
import com.google.gwt.event.dom.client.DoubleClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Window;
import com.vaadin.client.ApplicationConnection;
@@ -67,7 +70,7 @@ public class WindowConnector extends AbstractSingleComponentContainerConnector
};

abstract class WindowEventHandler
implements ClickHandler, DoubleClickHandler {
implements ClickHandler, DoubleClickHandler, KeyUpHandler {
}

private WindowEventHandler maximizeRestoreClickHandler = new WindowEventHandler() {
@@ -91,6 +94,18 @@ public class WindowConnector extends AbstractSingleComponentContainerConnector
onMaximizeRestore();
}
}

@Override
public void onKeyUp(KeyUpEvent event) {
final int keyCode = event.getNativeKeyCode();
final Element target = event.getNativeEvent().getEventTarget()
.cast();
// key ENTER or SPACE on maximize/restore box
if (target == getWidget().maximizeRestoreBox
&& isKeyEnterOrSpace(keyCode)) {
onMaximizeRestore();
}
}
};

@Override
@@ -115,6 +130,7 @@ public class WindowConnector extends AbstractSingleComponentContainerConnector
window.addHandler(maximizeRestoreClickHandler, ClickEvent.getType());
window.addHandler(maximizeRestoreClickHandler,
DoubleClickEvent.getType());
window.addHandler(maximizeRestoreClickHandler, KeyUpEvent.getType());

window.setOwner(getConnection().getUIConnector().getWidget());

@@ -511,4 +527,8 @@ public class WindowConnector extends AbstractSingleComponentContainerConnector
getRpcProxy(WindowServerRpc.class).windowMoved(event.getNewX(),
event.getNewY());
}

private boolean isKeyEnterOrSpace(int keyCode) {
return keyCode == KeyCodes.KEY_ENTER || keyCode == KeyCodes.KEY_SPACE;
}
}

+ 37
- 0
uitest/src/main/java/com/vaadin/tests/components/window/WindowHeaderButtonKeyboardActions.java View File

@@ -0,0 +1,37 @@
package com.vaadin.tests.components.window;

import com.vaadin.annotations.Widgetset;
import com.vaadin.server.VaadinRequest;
import com.vaadin.tests.components.AbstractTestUI;
import com.vaadin.ui.Button;
import com.vaadin.ui.Label;
import com.vaadin.ui.Window;

@Widgetset("com.vaadin.DefaultWidgetSet")
public class WindowHeaderButtonKeyboardActions extends AbstractTestUI {

@Override
protected void setup(VaadinRequest request) {
Button button = new Button("Open window");
button.setId("firstButton");

button.addClickListener(new Button.ClickListener() {
public void buttonClick(Button.ClickEvent event) {
Window window = new Window("WINDOW");
window.setContent(new Label("Inside window"));
window.setHeight("100px");
window.setId("testWindow");
window.addCloseListener(new Window.CloseListener() {
@Override
public void windowClose(Window.CloseEvent e) {

}
});
addWindow(window);
}
});
addComponent(button);

}

}

+ 405
- 0
uitest/src/test/java/com/vaadin/tests/components/window/WindowHeaderButtonKeyboardActionsTest.java View File

@@ -0,0 +1,405 @@
package com.vaadin.tests.components.window;

import static org.junit.Assert.assertTrue;

import java.io.IOException;

import org.junit.Test;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Actions;

import com.vaadin.testbench.By;
import com.vaadin.testbench.elements.WindowElement;
import com.vaadin.tests.tb3.MultiBrowserTest;

public class WindowHeaderButtonKeyboardActionsTest extends MultiBrowserTest {

private static final String HEADER_CLASS = "v-window-header";
private static final String RESTORE_BOX_CLASS = "v-window-restorebox";
private static final String MAXIMIZE_BOX_CLASS = "v-window-maximizebox";
private static final String CLOSE_BOX_CLASS = "v-window-closebox";

@Override
public void setup() throws Exception {
super.setup();
openTestURL();

// open window before each test case
waitForElementPresent(By.id("firstButton"));
WebElement button = findElement(By.id("firstButton"));
button.click();

waitForElementPresent(By.id("testWindow"));
}

/**
* Scenario: focus the close button of the opened window -> press ENTER key
* -> window should be closed
*/
@Test
public void testCloseWindowWithEnter() throws IOException {

assertTrue("Window is not open",
findElements(By.id("testWindow")).size() == 1);

WindowElement windowElement = $(WindowElement.class).first();
WebElement closeButton = windowElement
.findElement(By.className(CLOSE_BOX_CLASS));
setFocusToElementAndWait(closeButton);

assertTrue("Window's close button is not the focused element",
closeButton.equals(driver.switchTo().activeElement()));

pressKeyAndWait(Keys.ENTER);
assertTrue("Window is not closed",
findElements(By.className("v-window")).size() == 0);
}

/**
* Scenario: focus the close button of the opened window -> press SPACE key
* -> window should be closed
*/
@Test
public void testCloseWindowWithSpace() throws IOException {

assertTrue("Window is not open",
findElements(By.id("testWindow")).size() == 1);

WindowElement windowElement = $(WindowElement.class).first();
WebElement closeButton = windowElement
.findElement(By.className(CLOSE_BOX_CLASS));
setFocusToElementAndWait(closeButton);

assertTrue("Window's close button is not the focused element",
closeButton.equals(driver.switchTo().activeElement()));
pressKeyAndWait(Keys.SPACE);

assertTrue("Window is not closed",
findElements(By.className("v-window")).size() == 0);
}

/**
* Scenario: focus close button of opened window -> press keys DELETE,
* ARROW_LEFT, and END -> window should remain open after all actions
*/
@Test
public void testIncorrectKeyInputDoesntFireClose() throws IOException {

assertTrue("Window is not open",
findElements(By.id("testWindow")).size() == 1);

WindowElement windowElement = $(WindowElement.class).first();
WebElement closeButton = windowElement
.findElement(By.className(CLOSE_BOX_CLASS));
setFocusToElementAndWait(closeButton);

assertTrue("Window's close button is not the focused element",
closeButton.equals(driver.switchTo().activeElement()));

pressKeyAndWait(Keys.DELETE);
assertTrue(
"Window is closed by DELETE when close button is the focused element",
findElements(By.className("v-window")).size() > 0);

pressKeyAndWait(Keys.ARROW_LEFT);
assertTrue(
"Window is closed by ARROW_LEFT when close button is the focused element",
findElements(By.className("v-window")).size() > 0);

pressKeyAndWait(Keys.END);
assertTrue(
"Window is closed by END when close button is the focused element",
findElements(By.className("v-window")).size() > 0);
}

/**
* Scenario: close button of opened window is not focused -> press keys
* ENTER and SPACE -> window should remain open after all actions
*/
@Test
public void testNonfocusedKeyDoesntCloseWindow() throws IOException {

assertTrue("Window is not open",
findElements(By.id("testWindow")).size() == 1);

WindowElement windowElement = $(WindowElement.class).first();
WebElement closeButton = windowElement
.findElement(By.className(CLOSE_BOX_CLASS));
assertTrue("Window's close button is the focused element",
!closeButton.equals(driver.switchTo().activeElement()));

pressKeyAndWait(Keys.ENTER);
assertTrue(
"Window is closed by ENTER when close button is not the focused element",
findElements(By.className("v-window")).size() > 0);

pressKeyAndWait(Keys.SPACE);
assertTrue(
"Window is closed by SPACE when close button is not the focused element",
findElements(By.className("v-window")).size() > 0);
}

/**
* Scenario: focus close button of opened window -> press keys TAB, and
* TAB+SHIFT in succession, shifting focus from and back to the button ->
* press ENTER key -> window should be closed
*/
@Test
public void testShiftFocusAndCloseWindow() throws IOException {

assertTrue("Window is not open",
findElements(By.id("testWindow")).size() == 1);

WindowElement windowElement = $(WindowElement.class).first();
WebElement closeButton = windowElement
.findElement(By.className(CLOSE_BOX_CLASS));
setFocusToElementAndWait(closeButton);
assertTrue("Window's close button is not the focused element",
closeButton.equals(driver.switchTo().activeElement()));

pressKeyAndWait(Keys.TAB);
assertTrue("Window's close button is the focused element",
!closeButton.equals(driver.switchTo().activeElement()));
pressKeyAndWait(Keys.SHIFT, Keys.TAB);
assertTrue("Window's close button is not the focused element",
closeButton.equals(driver.switchTo().activeElement()));

pressKeyAndWait(Keys.ENTER);
assertTrue(
"Window is not closed when focus is shifted back-and-forth",
findElements(By.className("v-window")).size() == 0);
}

/**
* Scenario: focus close button of opened window -> click close button with
* the mouse cursor -> window should be closed
*/
@Test
public void testMouseClickClosesWindowOnFocus() throws IOException {

assertTrue("Window is not open",
findElements(By.id("testWindow")).size() == 1);

WindowElement windowElement = $(WindowElement.class).first();
WebElement closeButton = windowElement
.findElement(By.className(CLOSE_BOX_CLASS));
setFocusToElementAndWait(closeButton);
assertTrue("Window's close button is not the focused element",
closeButton.equals(driver.switchTo().activeElement()));

// click button with mouse and wait
closeButton.click();
sleep(200);

assertTrue("Window is not closed when focused element is clicked",
findElements(By.className("v-window")).size() == 0);
}

// Tests for maximize-restore button

/**
* Scenario: focus the maximize button of the opened window -> press ENTER
* key -> window should be maximized
*/
@Test
public void testMaximizeWindowWithEnter() throws IOException {

assertTrue("Window is not open",
findElements(By.id("testWindow")).size() == 1);

WindowElement windowElement = $(WindowElement.class).first();
WebElement closeButton = windowElement
.findElement(By.className(MAXIMIZE_BOX_CLASS));
setFocusToElementAndWait(closeButton);

assertTrue("Window's maximize button is not the focused element",
closeButton.equals(driver.switchTo().activeElement()));
pressKeyAndWait(Keys.ENTER);

assertTrue("Window is not maximized", windowElement.isMaximized());
}

/**
* Scenario: focus the maximize button of the opened window -> press SPACE
* key -> window should be maximized
*/
@Test
public void testMaximizeWindowWithSpace() throws IOException {

assertTrue("Window is not open",
findElements(By.id("testWindow")).size() == 1);

WindowElement windowElement = $(WindowElement.class).first();
WebElement closeButton = windowElement
.findElement(By.className(MAXIMIZE_BOX_CLASS));
setFocusToElementAndWait(closeButton);

assertTrue("Window's maximize button is not the focused element",
closeButton.equals(driver.switchTo().activeElement()));
pressKeyAndWait(Keys.SPACE);

assertTrue("Window is not maximized", windowElement.isMaximized());
}

/**
* Scenario: focus maximize button of opened window -> press keys DELETE,
* ARROW_UP, and ADD -> window should remain open after all actions
*/
@Test
public void testIncorrectKeyInputDoesntFireMaximize() throws IOException {

assertTrue("Window is not open",
findElements(By.id("testWindow")).size() == 1);

WindowElement windowElement = $(WindowElement.class).first();
WebElement maximizeButton = windowElement
.findElement(By.className(MAXIMIZE_BOX_CLASS));
setFocusToElementAndWait(maximizeButton);

assertTrue("Window's maximize button is not the focused element",
maximizeButton.equals(driver.switchTo().activeElement()));

pressKeyAndWait(Keys.DELETE);
assertTrue(
"Window is maximized by DELETE when maximize button is the focused element",
!windowElement.isMaximized());

pressKeyAndWait(Keys.ARROW_UP);
assertTrue(
"Window is cmaximized by ARROW_UP when maximize button is the focused element",
!windowElement.isMaximized());

pressKeyAndWait(Keys.ADD);
assertTrue(
"Window is maximized by ADD when maximize button is the focused element",
!windowElement.isMaximized());
}

/**
* Scenario: close button of opened window is not focused -> press keys
* ENTER and SPACE -> window should remain non-maximized after all actions
*/
@Test
public void testNonfocusedKeyDoesntMaximizeWindow() throws IOException {

assertTrue("Window is not open",
findElements(By.id("testWindow")).size() == 1);

WindowElement windowElement = $(WindowElement.class).first();
WebElement maximizeButton = windowElement
.findElement(By.className(MAXIMIZE_BOX_CLASS));
assertTrue("Window's close button is the focused element",
!maximizeButton.equals(driver.switchTo().activeElement()));

pressKeyAndWait(Keys.ENTER);
assertTrue(
"Window is maximized by ENTER when maximize button is not the focused element",
!windowElement.isMaximized());

pressKeyAndWait(Keys.SPACE);
assertTrue(
"Window is maximized by SPACE when maximize button is not the focused element",
!windowElement.isMaximized());
}

/**
* Scenario: focus maximize button of opened window -> press keys TAB, and
* TAB+SHIFT in succession, shifting focus from and back to the button ->
* press ENTER key -> window should be maximized
*/
@Test
public void testShiftFocusAndMaximizeWindow() throws IOException {

assertTrue("Window is not open",
findElements(By.id("testWindow")).size() == 1);

WindowElement windowElement = $(WindowElement.class).first();
WebElement maximizeButton = windowElement
.findElement(By.className(MAXIMIZE_BOX_CLASS));
setFocusToElementAndWait(maximizeButton);
assertTrue("Window's maximize button is not the focused element",
maximizeButton.equals(driver.switchTo().activeElement()));

pressKeyAndWait(Keys.TAB);
assertTrue("Window's maximize button is the focused element",
!maximizeButton.equals(driver.switchTo().activeElement()));
pressKeyAndWait(Keys.SHIFT, Keys.TAB);
assertTrue("Window's maximize button is not the focused element",
maximizeButton.equals(driver.switchTo().activeElement()));

pressKeyAndWait(Keys.ENTER);
assertTrue(
"Window is not maximized when focus is shifted back-and-forth",
windowElement.isMaximized());
}

/**
* Scenario: focus maximize button of opened window -> click maximize button
* with mouse cursor -> window should be maximized
*/
@Test
public void testMouseClickMaximizesWindowOnFocus() throws IOException {

assertTrue("Window is not open",
findElements(By.id("testWindow")).size() == 1);

WindowElement windowElement = $(WindowElement.class).first();
WebElement maximizeButton = windowElement
.findElement(By.className(MAXIMIZE_BOX_CLASS));
setFocusToElementAndWait(maximizeButton);
assertTrue("Window's maximize button is not the focused element",
maximizeButton.equals(driver.switchTo().activeElement()));

// click button with mouse and wait
maximizeButton.click();
sleep(100);

assertTrue("Window is not maximized when focused element is clicked",
windowElement.isMaximized());
}

/**
* Scenario: focus the maximize button of the opened window -> press ENTER
* key -> window should be maximized -> press ENTER key again -> window
* should be restored
*/
@Test
public void testMaximizeAndRestoreWindowWithEnter() throws IOException {

assertTrue("Window is not open",
findElements(By.id("testWindow")).size() == 1);

WindowElement windowElement = $(WindowElement.class).first();
WebElement closeButton = windowElement
.findElement(By.className(MAXIMIZE_BOX_CLASS));
setFocusToElementAndWait(closeButton);

assertTrue("Window's maximize button is not the focused element",
closeButton.equals(driver.switchTo().activeElement()));
pressKeyAndWait(Keys.ENTER);

assertTrue("Window is not maximized", windowElement.isMaximized());

assertTrue("Window's maximize button is not the focused element",
closeButton.equals(driver.switchTo().activeElement()));
pressKeyAndWait(Keys.ENTER);

assertTrue("Window remains maximized", !windowElement.isMaximized());
}

protected void setFocusToElementAndWait(WebElement element) {
String elementId = element.getAttribute("id");

((JavascriptExecutor) getDriver()).executeScript(
"document.getElementById('" + elementId + "').focus();",
element);
sleep(100);
}

protected void pressKeyAndWait(Keys... key) {
new Actions(driver).sendKeys(key).build().perform();
sleep(100);
}
}

Loading…
Cancel
Save