@@ -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(); | |||
} | |||
}; | |||
} | |||
/** |
@@ -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 -> { |
@@ -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); | |||
} | |||
} |
@@ -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<DesiredCapabilities> 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())); | |||
} | |||
} |