diff options
author | KatriHaapalinna <haapalinnakatri@gmail.com> | 2019-04-08 14:31:17 +0300 |
---|---|---|
committer | Sun Zhe <31067185+ZheSun88@users.noreply.github.com> | 2019-04-08 14:31:17 +0300 |
commit | 262863dbf6b33995d47bd9433e8f087d13a468ee (patch) | |
tree | fdb9d8fd7930d8b201e983b2cfe3541e74d66454 | |
parent | 7a010de76ec67362d5c9d395c79ce3e22613469f (diff) | |
download | vaadin-framework-262863dbf6b33995d47bd9433e8f087d13a468ee.tar.gz vaadin-framework-262863dbf6b33995d47bd9433e8f087d13a468ee.zip |
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
4 files changed, 474 insertions, 5 deletions
diff --git a/client/src/main/java/com/vaadin/client/ui/VWindow.java b/client/src/main/java/com/vaadin/client/ui/VWindow.java index f7fc2ba6b8..e19a13c005 100644 --- a/client/src/main/java/com/vaadin/client/ui/VWindow.java +++ b/client/src/main/java/com/vaadin/client/ui/VWindow.java @@ -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; + } } diff --git a/client/src/main/java/com/vaadin/client/ui/window/WindowConnector.java b/client/src/main/java/com/vaadin/client/ui/window/WindowConnector.java index feb34e502e..12ce828f47 100644 --- a/client/src/main/java/com/vaadin/client/ui/window/WindowConnector.java +++ b/client/src/main/java/com/vaadin/client/ui/window/WindowConnector.java @@ -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; + } } diff --git a/uitest/src/main/java/com/vaadin/tests/components/window/WindowHeaderButtonKeyboardActions.java b/uitest/src/main/java/com/vaadin/tests/components/window/WindowHeaderButtonKeyboardActions.java new file mode 100644 index 0000000000..4b5dc6de48 --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/window/WindowHeaderButtonKeyboardActions.java @@ -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); + + } + +} diff --git a/uitest/src/test/java/com/vaadin/tests/components/window/WindowHeaderButtonKeyboardActionsTest.java b/uitest/src/test/java/com/vaadin/tests/components/window/WindowHeaderButtonKeyboardActionsTest.java new file mode 100644 index 0000000000..47ac64107d --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/window/WindowHeaderButtonKeyboardActionsTest.java @@ -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); + } +} |