Browse Source

Fix/focus inside window chrome (#11142)

Fixes #11087
tags/8.6.0.alpha2
Mehdi Javan 5 years ago
parent
commit
269ce1beac

+ 48
- 37
client/src/main/java/com/vaadin/client/ui/ui/UIConnector.java View File

@@ -15,16 +15,6 @@
*/
package com.vaadin.client.ui.ui;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import com.google.gwt.core.client.Scheduler;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
@@ -101,9 +91,18 @@ import com.vaadin.shared.ui.ui.UIServerRpc;
import com.vaadin.shared.ui.ui.UIState;
import com.vaadin.shared.util.SharedUtil;
import com.vaadin.ui.UI;

import elemental.client.Browser;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

@Connect(value = UI.class, loadStyle = LoadStyle.EAGER)
public class UIConnector extends AbstractSingleComponentContainerConnector
implements Paintable, MayScrollChildren {
@@ -375,34 +374,18 @@ public class UIConnector extends AbstractSingleComponentContainerConnector
if (uidl.hasAttribute("focused")) {
// set focused component when render phase is finished
Scheduler.get().scheduleDeferred(() -> {
ComponentConnector connector = (ComponentConnector) uidl
.getPaintableAttribute("focused", getConnection());
Timer timer = new Timer() {
@Override
public void run() {
ComponentConnector connector = (ComponentConnector) uidl
.getPaintableAttribute("focused",
getConnection());

if (connector == null) {
// Do not try to focus invisible components which not
// present in UIDL
return;
}
focus(connector);
}
};

final Widget toBeFocused = connector.getWidget();
/*
* Two types of Widgets can be focused, either implementing GWT
* Focusable of a thinner Vaadin specific Focusable interface.
*/
if (toBeFocused instanceof com.google.gwt.user.client.ui.Focusable) {
final com.google.gwt.user.client.ui.Focusable toBeFocusedWidget = (com.google.gwt.user.client.ui.Focusable) toBeFocused;
toBeFocusedWidget.setFocus(true);
} else if (toBeFocused instanceof Focusable) {
((Focusable) toBeFocused).focus();
} else {
getLogger().severe(
"Server is trying to set focus to the widget of connector "
+ Util.getConnectorString(connector)
+ " but it is not focusable. The widget should implement either "
+ com.google.gwt.user.client.ui.Focusable.class
.getName()
+ " or " + Focusable.class.getName());
}
timer.schedule(0);
});
}

@@ -437,6 +420,34 @@ public class UIConnector extends AbstractSingleComponentContainerConnector
}
}

private void focus(ComponentConnector connector) {
if (connector == null) {
// Do not try to focus invisible components which not
// present in UIDL
return;
}

final Widget toBeFocused = connector.getWidget();
/*
* Two types of Widgets can be focused, either implementing GWT
* Focusable of a thinner Vaadin specific Focusable interface.
*/
if (toBeFocused instanceof com.google.gwt.user.client.ui.Focusable) {
final com.google.gwt.user.client.ui.Focusable toBeFocusedWidget = (com.google.gwt.user.client.ui.Focusable) toBeFocused;
toBeFocusedWidget.setFocus(true);
} else if (toBeFocused instanceof Focusable) {
((Focusable) toBeFocused).focus();
} else {
getLogger().severe(
"Server is trying to set focus to the widget of connector "
+ Util.getConnectorString(connector)
+ " but it is not focusable. The widget should implement either "
+ com.google.gwt.user.client.ui.Focusable.class
.getName()
+ " or " + Focusable.class.getName());
}
}

/**
* Reads CSS strings and resources injected by {@link Styles#inject} from
* the UIDL stream.

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

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

import com.vaadin.server.VaadinRequest;
import com.vaadin.tests.components.AbstractTestUI;
import com.vaadin.ui.Button;
import com.vaadin.ui.ComboBox;
import com.vaadin.ui.TextField;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;

public class InitialFocus extends AbstractTestUI {

public static final String FOCUS_NAME_BUTTON_ID = "focusNameButton";
public static final String FOCUS_GENDER_BUTTON_ID = "focusGenderButton";
public static final String NAME_FIELD_ID = "nameField";
public static final String GENDER_FIELD_ID = "genderField";

@Override
protected void setup(VaadinRequest request) {
Button focusNameButton = new Button("Open Window and focus Name");
focusNameButton.setId(FOCUS_NAME_BUTTON_ID);
focusNameButton.addClickListener(event -> {
MyDialog myDialog = new MyDialog();
myDialog.setClosable(true);
myDialog.center();
getUI().addWindow(myDialog);
myDialog.bringToFront();
myDialog.focusNameField();
});
addComponent(focusNameButton);

Button focusGenderButton = new Button("Open Window and focus Gender");
focusGenderButton.setId(FOCUS_GENDER_BUTTON_ID);
focusGenderButton.addClickListener(event -> {
MyDialog myDialog = new MyDialog();
myDialog.setClosable(true);
myDialog.center();
getUI().addWindow(myDialog);
myDialog.bringToFront();
myDialog.focusGenderField();
});
addComponent(focusGenderButton);
}

private static class MyDialog extends Window {
private TextField nameField;
private ComboBox genderField;

private MyDialog() {
super("MyDialog");
setWidth("400px");
setHeight("300px");
VerticalLayout hl = new VerticalLayout();
hl.setSizeFull();
nameField = new TextField("Name");
nameField.setId(NAME_FIELD_ID);
hl.addComponent(this.nameField);

genderField = new ComboBox("Gender");
genderField.setId(GENDER_FIELD_ID);
hl.addComponentsAndExpand(genderField);

this.setContent(hl);
}

private void focusNameField() {
nameField.focus();
}

private void focusGenderField() {
genderField.focus();
}
}
}

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

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

import com.vaadin.testbench.elements.ComboBoxElement;
import com.vaadin.tests.tb3.MultiBrowserTest;
import org.junit.Assert;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;

public class InitialFocusTest extends MultiBrowserTest {
/**
* To test an implementation of
* {@code com.google.gwt.user.client.ui.Focusable} which is nameField of
* TextField type
*/
@Test
public void window_callingFocusNameField_nameFieldShouldGetFocused() {
openTestURL();
WebElement openWindowButton = findElement(
By.id(InitialFocus.FOCUS_NAME_BUTTON_ID));
openWindowButton.click();
waitForElementPresent(By.id(InitialFocus.NAME_FIELD_ID));
WebElement focusedElement = getFocusedElement();
Assert.assertNotNull(
"Name TextField should have focus while focusedElement is null.",
focusedElement);
String focusedElementId = focusedElement.getAttribute("id");
Assert.assertEquals(
"Name TextField should have focus while " + focusedElementId
+ " has focus.",
InitialFocus.NAME_FIELD_ID, focusedElementId);
}

/**
* To test an implementation of {@code com.vaadin.client.Focusable} which is
* genderField of ComboBox type
*/
@Test
public void window_callingFocusGenderField_genderFieldShouldGetFocused() {
openTestURL();
WebElement openWindowButton = findElement(
By.id(InitialFocus.FOCUS_GENDER_BUTTON_ID));
openWindowButton.click();
waitForElementPresent(By.id(InitialFocus.GENDER_FIELD_ID));
WebElement focusedElement = getFocusedElement();
Assert.assertNotNull(
"Gender ComboBox should have focus while focusedElement is null.",
focusedElement);
ComboBoxElement genderField = $(ComboBoxElement.class).first();
Assert.assertEquals(
"Gender ComboBox should have focus while another element has focus.",
genderField.getInputField(), focusedElement);
}
}

Loading…
Cancel
Save