From d0bc2ad8f940506d2d2aa1a79725b5ed636a914b Mon Sep 17 00:00:00 2001 From: =?utf8?q?Olli=20Tiet=C3=A4v=C3=A4inen?= Date: Mon, 16 Apr 2018 17:56:16 +0300 Subject: [PATCH] Add a check for tab events trying to set focus outside a modal Window (#10655) --- .../java/com/vaadin/client/ui/VWindow.java | 26 ++++++++ .../components/window/ModalWindowFocus.java | 2 + .../window/ModalWindowFocusTest.java | 3 +- .../window/ModalWindowRefocusTest.java | 61 +++++++++++++++++++ 4 files changed, 90 insertions(+), 2 deletions(-) create mode 100755 uitest/src/test/java/com/vaadin/tests/components/window/ModalWindowRefocusTest.java 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 69ffa8a89a..746c432997 100644 --- a/client/src/main/java/com/vaadin/client/ui/VWindow.java +++ b/client/src/main/java/com/vaadin/client/ui/VWindow.java @@ -178,9 +178,11 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, private NativePreviewHandler topEventBlocker; private NativePreviewHandler bottomEventBlocker; + private NativePreviewHandler modalEventBlocker; private HandlerRegistration topBlockerRegistration; private HandlerRegistration bottomBlockerRegistration; + private HandlerRegistration modalBlockerRegistration; // Prevents leaving the window with the Tab key when true private boolean doTabStop; @@ -264,6 +266,8 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, .addNativePreviewHandler(topEventBlocker); bottomBlockerRegistration = Event .addNativePreviewHandler(bottomEventBlocker); + modalBlockerRegistration = Event + .addNativePreviewHandler(modalEventBlocker); } } @@ -274,6 +278,9 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, bottomBlockerRegistration.removeHandler(); bottomBlockerRegistration = null; + + modalBlockerRegistration.removeHandler(); + modalBlockerRegistration = null; } } @@ -468,6 +475,25 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, nativeEvent.preventDefault(); } }; + + // Handle modal window + tabbing when the focus is not inside the + // window (custom tab order or tabbing in from browser url bar) + modalEventBlocker = event -> { + if (!vaadinModality + || getElement().isOrHasChild(WidgetUtil.getFocusedElement()) + || (getTopmostWindow() != VWindow.this)) { + return; + } + + NativeEvent nativeEvent = event.getNativeEvent(); + if (nativeEvent.getType().equals("keyup") + && nativeEvent.getKeyCode() == KeyCodes.KEY_TAB) { + nativeEvent.preventDefault(); + focus(); + + } + }; + } /** diff --git a/uitest/src/main/java/com/vaadin/tests/components/window/ModalWindowFocus.java b/uitest/src/main/java/com/vaadin/tests/components/window/ModalWindowFocus.java index 38ce020b80..20a6e4cfc7 100644 --- a/uitest/src/main/java/com/vaadin/tests/components/window/ModalWindowFocus.java +++ b/uitest/src/main/java/com/vaadin/tests/components/window/ModalWindowFocus.java @@ -16,6 +16,7 @@ public class ModalWindowFocus extends AbstractReindeerTestUI { protected void setup(VaadinRequest req) { Button button = new Button("Open windows"); + button.setTabIndex(2); button.setId("firstButton"); addComponent(button); button.addClickListener(event -> { @@ -42,6 +43,7 @@ public class ModalWindowFocus extends AbstractReindeerTestUI { }); Button button2 = new Button( "Open unclosable and unresizable modal window"); + button2.setTabIndex(1); addComponent(button2); button2.setId("modalWindowButton"); button2.addClickListener(event -> { diff --git a/uitest/src/test/java/com/vaadin/tests/components/window/ModalWindowFocusTest.java b/uitest/src/test/java/com/vaadin/tests/components/window/ModalWindowFocusTest.java index b5bd7bcdcc..4bece63be6 100644 --- a/uitest/src/test/java/com/vaadin/tests/components/window/ModalWindowFocusTest.java +++ b/uitest/src/test/java/com/vaadin/tests/components/window/ModalWindowFocusTest.java @@ -96,7 +96,7 @@ public class ModalWindowFocusTest extends MultiBrowserTest { "this has been focused".equals(tfe.getValue())); } - private void pressKeyAndWait(Keys key) { + protected void pressKeyAndWait(Keys key) { new Actions(driver).sendKeys(key).build().perform(); sleep(100); } @@ -113,7 +113,6 @@ public class ModalWindowFocusTest extends MultiBrowserTest { assertEquals("true", ariaModal); String role = windowElement.getAttribute("role"); assertEquals("dialog", role); - } } diff --git a/uitest/src/test/java/com/vaadin/tests/components/window/ModalWindowRefocusTest.java b/uitest/src/test/java/com/vaadin/tests/components/window/ModalWindowRefocusTest.java new file mode 100755 index 0000000000..d09d3ce027 --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/window/ModalWindowRefocusTest.java @@ -0,0 +1,61 @@ +package com.vaadin.tests.components.window; + +import static org.junit.Assert.assertTrue; + +import java.util.List; + +import org.junit.Test; +import org.openqa.selenium.Keys; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.remote.DesiredCapabilities; + +import com.vaadin.testbench.By; +import com.vaadin.testbench.elements.TextFieldElement; +import com.vaadin.testbench.parallel.Browser; + +/** + * Tests that a modal window is focused on creation and that on closing a window + * focus is given to underlying modal window + * + * @author Vaadin Ltd + */ +public class ModalWindowRefocusTest extends ModalWindowFocusTest { + + @Override + public List getBrowsersToTest() { + // Chrome doesn't support clicking on the modality curtain + return getBrowserCapabilities(Browser.IE11, Browser.EDGE, + Browser.FIREFOX); + } + + @Override + protected Class getUIClass() { + return ModalWindowFocus.class; + } + + /** + * Open modal window -> click modality curtain to remove focus from Window + * -> press tab thrice so that focus goes into Window again and focuses the + * text field so that the focus event is fired. + */ + @Test + public void testFocusOutsideModal() { + waitForElementPresent(By.id("modalWindowButton")); + WebElement button = findElement(By.id("modalWindowButton")); + button.click(); + waitForElementPresent(By.id("focusfield")); + WebElement curtain = findElement( + org.openqa.selenium.By.className("v-window-modalitycurtain")); + curtain.click(); + + pressKeyAndWait(Keys.TAB); + pressKeyAndWait(Keys.TAB); + pressKeyAndWait(Keys.TAB); + + TextFieldElement tfe = $(TextFieldElement.class).id("focusfield"); + assertTrue("First TextField should have received focus", + "this has been focused".equals(tfe.getValue())); + + } + +} -- 2.39.5