Fixes #10429 * implement focus handing in RadioButtonGroup, fixes #10429 * Merge branch 'master' of https://github.com/vaadin/framework into radiobuttongroup-focus * merge * Merge branch 'master' of https://github.com/vaadin/framework into radiobuttongroup-focus * fix initial focus handling also on CheckBoxGroup and add tests * add license headers * Merge branch 'master' of https://github.com/vaadin/framework into radiobuttongroup-focus * changed client to use lambdas and refactored focus testing to parent class * made FocusTest abstract * Merge branch 'master' of https://github.com/vaadin/framework into radiobuttongroup-focus * don't allow focusing on disabled items & refactor focusing first itemtags/8.4.0.alpha1
@@ -23,6 +23,7 @@ import java.util.Map; | |||
import java.util.function.BiConsumer; | |||
import com.google.gwt.aria.client.Roles; | |||
import com.google.gwt.core.client.Scheduler; | |||
import com.google.gwt.dom.client.Element; | |||
import com.google.gwt.event.dom.client.ClickEvent; | |||
import com.google.gwt.event.dom.client.ClickHandler; | |||
@@ -235,4 +236,14 @@ public class VCheckBoxGroup extends FocusableFlowPanelComposite | |||
.remove(selectionChanged); | |||
} | |||
/** | |||
* Set focus to the first check box. | |||
*/ | |||
@Override | |||
public void focus() { | |||
// If focus is set on creation, need to wait until options are populated | |||
Scheduler.get().scheduleDeferred(() -> { | |||
getWidget().focusFirstEnabledChild(); | |||
}); | |||
} | |||
} |
@@ -24,6 +24,7 @@ import java.util.Optional; | |||
import java.util.function.Consumer; | |||
import com.google.gwt.aria.client.Roles; | |||
import com.google.gwt.core.client.Scheduler; | |||
import com.google.gwt.dom.client.Element; | |||
import com.google.gwt.event.dom.client.ClickEvent; | |||
import com.google.gwt.event.dom.client.ClickHandler; | |||
@@ -40,6 +41,7 @@ import com.vaadin.client.widgets.FocusableFlowPanelComposite; | |||
import com.vaadin.shared.Registration; | |||
import com.vaadin.shared.data.DataCommunicatorConstants; | |||
import com.vaadin.shared.ui.ListingJsonConstants; | |||
import elemental.json.JsonObject; | |||
/** | |||
@@ -271,9 +273,30 @@ public class VRadioButtonGroup extends FocusableFlowPanelComposite | |||
} | |||
} | |||
/** | |||
* Set focus to the selected radio button (or first radio button if there is | |||
* no selection). | |||
*/ | |||
@Override | |||
public void focus() { | |||
// If focus is set on creation, need to wait until options are populated | |||
Scheduler.get().scheduleDeferred(() -> { | |||
// if there's a selected radio button, focus it | |||
for (String key : keyToOptions.keySet()) { | |||
RadioButton radioButton = keyToOptions.get(key); | |||
if (radioButton != null && radioButton.getValue()) { | |||
radioButton.setFocus(true); | |||
return; | |||
} | |||
} | |||
// otherwise focus the first enabled child | |||
getWidget().focusFirstEnabledChild(); | |||
}); | |||
} | |||
/** | |||
* Updates the selected state of a radio button. | |||
* | |||
* | |||
* @param radioButton | |||
* the radio button to update | |||
* @param value | |||
@@ -282,5 +305,6 @@ public class VRadioButtonGroup extends FocusableFlowPanelComposite | |||
protected void updateItemSelection(RadioButton radioButton, boolean value) { | |||
radioButton.setValue(value); | |||
radioButton.setStyleName(CLASSNAME_OPTION_SELECTED, value); | |||
} | |||
} |
@@ -153,6 +153,26 @@ public class ChildFocusAwareFlowPanel extends FocusableFlowPanel | |||
} | |||
} | |||
/** | |||
* Put focus in the first child Widget that can be focused and is not | |||
* disabled. | |||
*/ | |||
public void focusFirstEnabledChild() { | |||
for (int i = 0; i < getWidgetCount(); i++) { | |||
Widget widget = getWidget(i); | |||
if (!(widget instanceof FocusWidget)) { | |||
continue; | |||
} | |||
FocusWidget focusableChild = (FocusWidget) widget; | |||
if (focusableChild.isEnabled()) { | |||
focusableChild.setFocus(true); | |||
break; | |||
} | |||
} | |||
} | |||
private void addHandlers(Widget widget) { | |||
if (focusRegistrations.containsKey(widget)) { | |||
assert blurRegistrations.containsKey(widget); |
@@ -0,0 +1,40 @@ | |||
/* | |||
* Copyright 2000-2016 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.tests.components.checkboxgroup; | |||
import com.vaadin.annotations.Widgetset; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.tests.components.AbstractTestUIWithLog; | |||
import com.vaadin.ui.Button; | |||
import com.vaadin.ui.CheckBoxGroup; | |||
@Widgetset("com.vaadin.DefaultWidgetSet") | |||
public class CheckBoxGroupFocus extends AbstractTestUIWithLog { | |||
@Override | |||
protected void setup(VaadinRequest request) { | |||
CheckBoxGroup<String> cbg = new CheckBoxGroup<>("CheckBoxes"); | |||
cbg.setItems("Test1", "Test2", "Test3"); | |||
cbg.select("Test2"); | |||
cbg.setItemCaptionGenerator(item -> "Option " + item); | |||
cbg.focus(); | |||
CheckBoxGroup<String> cbg2 = new CheckBoxGroup<>("No selection"); | |||
cbg2.setItems("Foo1", "Foo2", "Foo3"); | |||
Button button = new Button("focus second group", e -> cbg2.focus()); | |||
addComponents(cbg, cbg2, button); | |||
} | |||
} |
@@ -0,0 +1,44 @@ | |||
/* | |||
* Copyright 2000-2016 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.tests.components.radiobuttongroup; | |||
import com.vaadin.annotations.Widgetset; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.tests.components.AbstractTestUIWithLog; | |||
import com.vaadin.ui.Button; | |||
import com.vaadin.ui.RadioButtonGroup; | |||
/** | |||
* @author Vaadin Ltd | |||
* | |||
*/ | |||
@Widgetset("com.vaadin.DefaultWidgetSet") | |||
public class RadioButtonGroupFocus extends AbstractTestUIWithLog { | |||
@Override | |||
protected void setup(VaadinRequest request) { | |||
RadioButtonGroup<String> rbg = new RadioButtonGroup<>("Radios"); | |||
rbg.setItems("Test1", "Test2", "Test3"); | |||
rbg.setSelectedItem("Test2"); | |||
rbg.setItemCaptionGenerator(item -> "Option " + item); | |||
rbg.focus(); | |||
RadioButtonGroup<String> rbg2 = new RadioButtonGroup<>("No selection"); | |||
rbg2.setItems("Foo1", "Foo2", "Foo3"); | |||
Button button = new Button("focus second group", e -> rbg2.focus()); | |||
addComponents(rbg, rbg2, button); | |||
} | |||
} |
@@ -0,0 +1,47 @@ | |||
/* | |||
* Copyright 2000-2016 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.tests.components; | |||
import static org.junit.Assert.assertNotNull; | |||
import static org.junit.Assert.assertTrue; | |||
import java.util.List; | |||
import org.openqa.selenium.By; | |||
import org.openqa.selenium.WebElement; | |||
import org.openqa.selenium.remote.DesiredCapabilities; | |||
import com.vaadin.testbench.TestBenchElement; | |||
import com.vaadin.tests.tb3.MultiBrowserTest; | |||
public abstract class FocusTest extends MultiBrowserTest { | |||
protected boolean isFocusInsideElement(TestBenchElement element) { | |||
WebElement focused = getFocusedElement(); | |||
assertNotNull(focused); | |||
String id = focused.getAttribute("id"); | |||
assertTrue("Focused element should have a non-empty id", | |||
id != null && !"".equals(id)); | |||
return element.isElementPresent(By.id(id)); | |||
} | |||
@Override | |||
public List<DesiredCapabilities> getBrowsersToTest() { | |||
// Focus does not move when expected with Selenium/TB and Firefox 45 | |||
return getBrowsersExcludingFirefox(); | |||
} | |||
} |
@@ -0,0 +1,45 @@ | |||
/* | |||
* Copyright 2000-2016 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.tests.components.checkboxgroup; | |||
import static org.junit.Assert.assertTrue; | |||
import org.junit.Test; | |||
import com.vaadin.testbench.elements.ButtonElement; | |||
import com.vaadin.testbench.elements.CheckBoxGroupElement; | |||
import com.vaadin.tests.components.FocusTest; | |||
public class CheckBoxGroupFocusTest extends FocusTest { | |||
@Test | |||
public void focusOnInit() { | |||
openTestURL(); | |||
CheckBoxGroupElement checkBoxGroup = $(CheckBoxGroupElement.class) | |||
.first(); | |||
assertTrue(isFocusInsideElement(checkBoxGroup)); | |||
} | |||
@Test | |||
public void moveFocusAfterClick() { | |||
openTestURL(); | |||
$(ButtonElement.class).first().click(); | |||
CheckBoxGroupElement checkBoxGroup = $(CheckBoxGroupElement.class) | |||
.last(); | |||
assertTrue(isFocusInsideElement(checkBoxGroup)); | |||
} | |||
} |
@@ -0,0 +1,45 @@ | |||
/* | |||
* Copyright 2000-2016 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.tests.components.radiobuttongroup; | |||
import static org.junit.Assert.assertTrue; | |||
import org.junit.Test; | |||
import com.vaadin.testbench.elements.ButtonElement; | |||
import com.vaadin.testbench.elements.RadioButtonGroupElement; | |||
import com.vaadin.tests.components.FocusTest; | |||
public class RadioButtonGroupFocusTest extends FocusTest { | |||
@Test | |||
public void focusOnInit() { | |||
openTestURL(); | |||
RadioButtonGroupElement radioButtonGroup = $( | |||
RadioButtonGroupElement.class).first(); | |||
assertTrue(isFocusInsideElement(radioButtonGroup)); | |||
} | |||
@Test | |||
public void moveFocusAfterClick() { | |||
openTestURL(); | |||
$(ButtonElement.class).first().click(); | |||
RadioButtonGroupElement radioButtonGroup2 = $( | |||
RadioButtonGroupElement.class).last(); | |||
assertTrue(isFocusInsideElement(radioButtonGroup2)); | |||
} | |||
} |