* Make AbstractListing implement Focusable Fixes vaadin/framework8-issues#552 * Add tests for other components that inherit from AbstractListing * Fix setTabIndex in NativeSelect, ListSelect, RadioButtonGroup Also adds a test for TwinColSelect.tags/8.0.0.alpha10
@@ -59,6 +59,16 @@ public class VNativeSelect extends FocusableFlowPanelComposite | |||
} | |||
} | |||
/** | |||
* Sets the tab index. | |||
* | |||
* @param tabIndex | |||
* the tab index to set | |||
*/ | |||
public void setTabIndex(int tabIndex) { | |||
getListBox().setTabIndex(tabIndex); | |||
} | |||
/** | |||
* Gets the underlying ListBox widget that this widget wraps. | |||
* |
@@ -51,4 +51,8 @@ public class ListSelectConnector extends AbstractMultiSelectConnector { | |||
getWidget().setReadOnly(isReadOnly()); | |||
} | |||
@OnStateChange("tabIndex") | |||
void updateTabIndex() { | |||
getWidget().setTabIndex(getState().tabIndex); | |||
} | |||
} |
@@ -77,7 +77,6 @@ public class NativeSelectConnector | |||
} | |||
@OnStateChange("readOnly") | |||
@SuppressWarnings("deprecation") | |||
void updateWidgetReadOnly() { | |||
getWidget().getListBox().setEnabled(isEnabled() && !isReadOnly()); | |||
} | |||
@@ -87,6 +86,11 @@ public class NativeSelectConnector | |||
getWidget().setSelectedItem(getState().selectedItemKey); | |||
} | |||
@OnStateChange("tabIndex") | |||
void updateTabIndex() { | |||
getWidget().setTabIndex(getState().tabIndex); | |||
} | |||
@Override | |||
public NativeSelectState getState() { | |||
return (NativeSelectState) super.getState(); |
@@ -61,6 +61,7 @@ public class RadioButtonGroupConnector | |||
@Override | |||
public void onStateChanged(StateChangeEvent stateChangeEvent) { | |||
super.onStateChanged(stateChangeEvent); | |||
getWidget().setTabIndex(getState().tabIndex); | |||
getWidget().client = getConnection(); | |||
} | |||
@@ -70,6 +70,11 @@ public class TwinColSelectConnector extends AbstractMultiSelectConnector | |||
getWidget().setReadOnly(isReadOnly()); | |||
} | |||
@OnStateChange("tabIndex") | |||
void updateTabIndex() { | |||
getWidget().setTabIndex(getState().tabIndex); | |||
} | |||
@Override | |||
public void layoutVertically() { | |||
if (isUndefinedHeight()) { |
@@ -31,6 +31,7 @@ import com.vaadin.server.data.DataProvider; | |||
import com.vaadin.server.data.Query; | |||
import com.vaadin.shared.extension.abstractlisting.AbstractListingExtensionState; | |||
import com.vaadin.shared.ui.abstractlisting.AbstractListingState; | |||
import com.vaadin.ui.Component.Focusable; | |||
import com.vaadin.ui.declarative.DesignAttributeHandler; | |||
import com.vaadin.ui.declarative.DesignContext; | |||
import com.vaadin.ui.declarative.DesignException; | |||
@@ -51,7 +52,8 @@ import com.vaadin.ui.declarative.DesignFormatter; | |||
* | |||
* @see Listing | |||
*/ | |||
public abstract class AbstractListing<T> extends AbstractComponent { | |||
public abstract class AbstractListing<T> extends AbstractComponent | |||
implements Focusable { | |||
/** | |||
* The item icon caption provider. | |||
*/ | |||
@@ -497,4 +499,19 @@ public abstract class AbstractListing<T> extends AbstractComponent { | |||
protected AbstractListingState getState(boolean markAsDirty) { | |||
return (AbstractListingState) super.getState(markAsDirty); | |||
} | |||
@Override | |||
public void focus() { | |||
super.focus(); | |||
} | |||
@Override | |||
public int getTabIndex() { | |||
return getState(false).tabIndex; | |||
} | |||
@Override | |||
public void setTabIndex(int tabIndex) { | |||
getState().tabIndex = tabIndex; | |||
} | |||
} |
@@ -1015,8 +1015,8 @@ public interface Component extends ClientConnector, Sizeable, Serializable { | |||
* <p> | |||
* Notice that this interface does not provide an accessor that would | |||
* allow finding out the currently focused component. Focus information | |||
* can be acquired for some (but not all) {@code LegacyField} components | |||
* through the {@link com.vaadin.event.FieldEvents.FocusListener} and | |||
* can be acquired for some (but not all) components through the | |||
* {@link com.vaadin.event.FieldEvents.FocusListener} and | |||
* {@link com.vaadin.event.FieldEvents.BlurListener} interfaces. | |||
* </p> | |||
* |
@@ -438,6 +438,12 @@ public class GridBasics extends AbstractTestUIWithLog { | |||
enableItem.setChecked(true); | |||
createSelectionMenu(stateMenu); | |||
stateMenu.addItem("Set focus", item -> grid.focus()); | |||
MenuItem tabIndexMenu = stateMenu.addItem("Tab index", null); | |||
addGridMethodMenu(tabIndexMenu, "0", 0, grid::setTabIndex); | |||
addGridMethodMenu(tabIndexMenu, "-1", -1, grid::setTabIndex); | |||
addGridMethodMenu(tabIndexMenu, "10", 10, grid::setTabIndex); | |||
} | |||
private void createRowStyleMenu(MenuItem rowStyleMenu) { |
@@ -0,0 +1,45 @@ | |||
package com.vaadin.tests.focusable; | |||
import static org.junit.Assert.assertEquals; | |||
import static org.junit.Assert.assertTrue; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import org.openqa.selenium.WebElement; | |||
import com.vaadin.tests.tb3.MultiBrowserTest; | |||
public abstract class AbstractFocusableComponentTest extends MultiBrowserTest { | |||
@Before | |||
public void setUp() { | |||
openTestURL(); | |||
} | |||
@Test | |||
public void testProgrammaticFocus() { | |||
selectMenuPath("Component", "State", "Set focus"); | |||
assertTrue("Component should be focused", isFocused()); | |||
} | |||
@Test | |||
public void testTabIndex() { | |||
assertEquals("0", getTabIndex()); | |||
selectMenuPath("Component", "State", "Tab index", "-1"); | |||
assertEquals("-1", getTabIndex()); | |||
selectMenuPath("Component", "State", "Tab index", "10"); | |||
assertEquals("10", getTabIndex()); | |||
} | |||
protected String getTabIndex() { | |||
return getFocusElement().getAttribute("tabindex"); | |||
} | |||
protected boolean isFocused() { | |||
return getFocusElement().equals(getDriver().switchTo().activeElement()); | |||
} | |||
protected abstract WebElement getFocusElement(); | |||
} |
@@ -0,0 +1,19 @@ | |||
package com.vaadin.tests.focusable; | |||
import org.openqa.selenium.By; | |||
import org.openqa.selenium.WebElement; | |||
import com.vaadin.tests.components.checkbox.CheckBoxGroupTestUI; | |||
public class CheckBoxGroupFocusableTest extends AbstractFocusableComponentTest { | |||
@Override | |||
protected Class<?> getUIClass() { | |||
return CheckBoxGroupTestUI.class; | |||
} | |||
@Override | |||
protected WebElement getFocusElement() { | |||
return findElement(By.xpath("//input[@type='checkbox']")); | |||
} | |||
} |
@@ -0,0 +1,28 @@ | |||
package com.vaadin.tests.focusable; | |||
import com.vaadin.testbench.customelements.GridElement; | |||
import com.vaadin.testbench.elements.GridElement.GridCellElement; | |||
import com.vaadin.tests.components.grid.basics.GridBasics; | |||
public class GridFocusableTest extends AbstractFocusableComponentTest { | |||
@Override | |||
protected Class<?> getUIClass() { | |||
return GridBasics.class; | |||
} | |||
@Override | |||
protected String getTabIndex() { | |||
return $(GridElement.class).first().getAttribute("tabindex"); | |||
} | |||
@Override | |||
protected boolean isFocused() { | |||
return getFocusElement().isFocused(); | |||
} | |||
@Override | |||
protected GridCellElement getFocusElement() { | |||
return $(GridElement.class).first().getCell(0, 0); | |||
} | |||
} |
@@ -0,0 +1,19 @@ | |||
package com.vaadin.tests.focusable; | |||
import org.openqa.selenium.WebElement; | |||
import com.vaadin.testbench.By; | |||
import com.vaadin.tests.components.listselect.ListSelectTestUI; | |||
public class ListSelectFocusableTest extends AbstractFocusableComponentTest { | |||
@Override | |||
protected Class<?> getUIClass() { | |||
return ListSelectTestUI.class; | |||
} | |||
@Override | |||
protected WebElement getFocusElement() { | |||
return findElement(By.className("v-select-select")); | |||
} | |||
} |
@@ -0,0 +1,19 @@ | |||
package com.vaadin.tests.focusable; | |||
import org.openqa.selenium.WebElement; | |||
import com.vaadin.testbench.By; | |||
import com.vaadin.tests.components.nativeselect.NativeSelects; | |||
public class NativeSelectFocusableTest extends AbstractFocusableComponentTest { | |||
@Override | |||
protected Class<?> getUIClass() { | |||
return NativeSelects.class; | |||
} | |||
@Override | |||
protected WebElement getFocusElement() { | |||
return findElement(By.className("v-select-select")); | |||
} | |||
} |
@@ -0,0 +1,19 @@ | |||
package com.vaadin.tests.focusable; | |||
import org.openqa.selenium.By; | |||
import org.openqa.selenium.WebElement; | |||
import com.vaadin.tests.components.radiobutton.RadioButtonGroupTestUI; | |||
public class RadioButtonGroupFocusableTest extends AbstractFocusableComponentTest { | |||
@Override | |||
protected Class<?> getUIClass() { | |||
return RadioButtonGroupTestUI.class; | |||
} | |||
@Override | |||
protected WebElement getFocusElement() { | |||
return findElement(By.xpath("//input[@type='radio']")); | |||
} | |||
} |
@@ -0,0 +1,20 @@ | |||
package com.vaadin.tests.focusable; | |||
import org.openqa.selenium.WebElement; | |||
import com.vaadin.testbench.By; | |||
import com.vaadin.tests.components.twincolselect.TwinColSelectTestUI; | |||
public class TwinColSelectFocusableTest | |||
extends AbstractFocusableComponentTest { | |||
@Override | |||
protected Class<?> getUIClass() { | |||
return TwinColSelectTestUI.class; | |||
} | |||
@Override | |||
protected WebElement getFocusElement() { | |||
return findElement(By.className("v-select-twincol-options")); | |||
} | |||
} |