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
import java.util.function.BiConsumer; | import java.util.function.BiConsumer; | ||||
import com.google.gwt.aria.client.Roles; | 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.dom.client.Element; | ||||
import com.google.gwt.event.dom.client.ClickEvent; | import com.google.gwt.event.dom.client.ClickEvent; | ||||
import com.google.gwt.event.dom.client.ClickHandler; | import com.google.gwt.event.dom.client.ClickHandler; | ||||
.remove(selectionChanged); | .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(); | |||||
}); | |||||
} | |||||
} | } |
import java.util.function.Consumer; | import java.util.function.Consumer; | ||||
import com.google.gwt.aria.client.Roles; | 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.dom.client.Element; | ||||
import com.google.gwt.event.dom.client.ClickEvent; | import com.google.gwt.event.dom.client.ClickEvent; | ||||
import com.google.gwt.event.dom.client.ClickHandler; | import com.google.gwt.event.dom.client.ClickHandler; | ||||
import com.vaadin.shared.Registration; | import com.vaadin.shared.Registration; | ||||
import com.vaadin.shared.data.DataCommunicatorConstants; | import com.vaadin.shared.data.DataCommunicatorConstants; | ||||
import com.vaadin.shared.ui.ListingJsonConstants; | import com.vaadin.shared.ui.ListingJsonConstants; | ||||
import elemental.json.JsonObject; | import elemental.json.JsonObject; | ||||
/** | /** | ||||
} | } | ||||
} | } | ||||
/** | |||||
* 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. | * Updates the selected state of a radio button. | ||||
* | |||||
* | |||||
* @param radioButton | * @param radioButton | ||||
* the radio button to update | * the radio button to update | ||||
* @param value | * @param value | ||||
protected void updateItemSelection(RadioButton radioButton, boolean value) { | protected void updateItemSelection(RadioButton radioButton, boolean value) { | ||||
radioButton.setValue(value); | radioButton.setValue(value); | ||||
radioButton.setStyleName(CLASSNAME_OPTION_SELECTED, value); | radioButton.setStyleName(CLASSNAME_OPTION_SELECTED, value); | ||||
} | } | ||||
} | } |
} | } | ||||
} | } | ||||
/** | |||||
* 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) { | private void addHandlers(Widget widget) { | ||||
if (focusRegistrations.containsKey(widget)) { | if (focusRegistrations.containsKey(widget)) { | ||||
assert blurRegistrations.containsKey(widget); | assert blurRegistrations.containsKey(widget); |
/* | |||||
* 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); | |||||
} | |||||
} |
/* | |||||
* 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); | |||||
} | |||||
} |
/* | |||||
* 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(); | |||||
} | |||||
} |
/* | |||||
* 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)); | |||||
} | |||||
} |
/* | |||||
* 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)); | |||||
} | |||||
} |