Fixes vaadin/framework8-issues#332 Change-Id: I19996ea83ed1fbe2b115d92d6be5e6a5e158f283tags/8.0.0.alpha6
@@ -0,0 +1,56 @@ | |||
/* | |||
* 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.client.connectors; | |||
import com.google.gwt.event.dom.client.HasAllFocusHandlers; | |||
import com.google.gwt.user.client.ui.Widget; | |||
import com.vaadin.client.ui.ConnectorFocusAndBlurHandler; | |||
import com.vaadin.shared.data.selection.SelectionModel; | |||
/** | |||
* Abstract class for listing widget connectors that contains focusable children | |||
* to track their focus/blur events. | |||
* | |||
* @author Vaadin Ltd | |||
* | |||
* @param <WIDGET> | |||
* widget type which has to allow to register focus/blur handlers | |||
* @param <SELECTIONMODEL> | |||
* the client-side selection model type | |||
*/ | |||
public abstract class AbstractFocusableListingConnector<WIDGET extends Widget & HasAllFocusHandlers, SELECTIONMODEL extends SelectionModel<?>> | |||
extends AbstractListingConnector<SELECTIONMODEL> { | |||
private ConnectorFocusAndBlurHandler handler; | |||
@Override | |||
protected void init() { | |||
handler = ConnectorFocusAndBlurHandler.addHandlers(this); | |||
} | |||
@Override | |||
public void onUnregister() { | |||
super.onUnregister(); | |||
handler.removeHandlers(); | |||
handler = null; | |||
} | |||
@SuppressWarnings("unchecked") | |||
@Override | |||
public WIDGET getWidget() { | |||
return (WIDGET) super.getWidget(); | |||
} | |||
} |
@@ -23,19 +23,14 @@ import java.util.Map; | |||
import java.util.function.BiConsumer; | |||
import com.google.gwt.aria.client.Roles; | |||
import com.google.gwt.event.dom.client.BlurHandler; | |||
import com.google.gwt.event.dom.client.ClickEvent; | |||
import com.google.gwt.event.dom.client.ClickHandler; | |||
import com.google.gwt.event.dom.client.FocusHandler; | |||
import com.google.gwt.event.dom.client.HasAllFocusHandlers; | |||
import com.google.gwt.event.shared.HandlerRegistration; | |||
import com.google.gwt.user.client.ui.Composite; | |||
import com.google.gwt.user.client.ui.FocusWidget; | |||
import com.google.gwt.user.client.ui.HasEnabled; | |||
import com.google.gwt.user.client.ui.Widget; | |||
import com.vaadin.client.ApplicationConnection; | |||
import com.vaadin.client.WidgetUtil; | |||
import com.vaadin.client.widgets.ChildFocusAwareFlowPanel; | |||
import com.vaadin.client.widgets.FocusableFlowPanelComposite; | |||
import com.vaadin.shared.Registration; | |||
import com.vaadin.shared.ui.ListingJsonConstants; | |||
@@ -47,8 +42,8 @@ import elemental.json.JsonObject; | |||
* @author Vaadin Ltd. | |||
* @since 8.0 | |||
*/ | |||
public class VCheckBoxGroup extends Composite implements Field, ClickHandler, | |||
com.vaadin.client.Focusable, HasEnabled, HasAllFocusHandlers { | |||
public class VCheckBoxGroup extends FocusableFlowPanelComposite implements | |||
Field, ClickHandler, com.vaadin.client.Focusable, HasEnabled { | |||
public static final String CLASSNAME = "v-select-optiongroup"; | |||
public static final String CLASSNAME_OPTION = "v-select-option"; | |||
@@ -60,14 +55,6 @@ public class VCheckBoxGroup extends Composite implements Field, ClickHandler, | |||
*/ | |||
public ApplicationConnection client; | |||
/** | |||
* Widget holding the different options (e.g. ListBox or Panel for radio | |||
* buttons) (optional, fallbacks to container Panel) | |||
* <p> | |||
* For internal use only. May be removed or replaced in the future. | |||
*/ | |||
private ChildFocusAwareFlowPanel optionsContainer; | |||
private boolean htmlContentAllowed = false; | |||
private boolean enabled; | |||
@@ -75,9 +62,7 @@ public class VCheckBoxGroup extends Composite implements Field, ClickHandler, | |||
private List<BiConsumer<JsonObject, Boolean>> selectionChangeListeners; | |||
public VCheckBoxGroup() { | |||
optionsContainer = new ChildFocusAwareFlowPanel(); | |||
initWidget(optionsContainer); | |||
optionsContainer.setStyleName(CLASSNAME); | |||
getWidget().setStyleName(CLASSNAME); | |||
optionsToItems = new HashMap<>(); | |||
selectionChangeListeners = new ArrayList<>(); | |||
} | |||
@@ -88,12 +73,12 @@ public class VCheckBoxGroup extends Composite implements Field, ClickHandler, | |||
public void buildOptions(List<JsonObject> items) { | |||
Roles.getGroupRole().set(getElement()); | |||
int i = 0; | |||
int widgetsToRemove = optionsContainer.getWidgetCount() - items.size(); | |||
int widgetsToRemove = getWidget().getWidgetCount() - items.size(); | |||
if (widgetsToRemove < 0) { | |||
widgetsToRemove = 0; | |||
} | |||
List<Widget> remove = new ArrayList<>(widgetsToRemove); | |||
for (Widget widget : optionsContainer) { | |||
for (Widget widget : getWidget()) { | |||
if (i < items.size()) { | |||
updateItem((VCheckBox) widget, items.get(i), false); | |||
i++; | |||
@@ -109,7 +94,7 @@ public class VCheckBoxGroup extends Composite implements Field, ClickHandler, | |||
} | |||
private void remove(Widget widget) { | |||
optionsContainer.remove(widget); | |||
getWidget().remove(widget); | |||
optionsToItems.remove(widget); | |||
} | |||
@@ -135,7 +120,7 @@ public class VCheckBoxGroup extends Composite implements Field, ClickHandler, | |||
if (requireInitializations) { | |||
widget.addStyleName(CLASSNAME_OPTION); | |||
widget.addClickHandler(this); | |||
optionsContainer.add(widget); | |||
getWidget().add(widget); | |||
} | |||
optionsToItems.put(widget, item); | |||
} | |||
@@ -161,7 +146,7 @@ public class VCheckBoxGroup extends Composite implements Field, ClickHandler, | |||
} | |||
public void setTabIndex(int tabIndex) { | |||
for (Widget anOptionsContainer : optionsContainer) { | |||
for (Widget anOptionsContainer : getWidget()) { | |||
FocusWidget widget = (FocusWidget) anOptionsContainer; | |||
widget.setTabIndex(tabIndex); | |||
} | |||
@@ -185,7 +170,7 @@ public class VCheckBoxGroup extends Composite implements Field, ClickHandler, | |||
@Override | |||
public void focus() { | |||
optionsContainer.focus(); | |||
getWidget().focus(); | |||
} | |||
public boolean isHtmlContentAllowed() { | |||
@@ -227,13 +212,4 @@ public class VCheckBoxGroup extends Composite implements Field, ClickHandler, | |||
.remove(selectionChanged); | |||
} | |||
@Override | |||
public HandlerRegistration addFocusHandler(FocusHandler handler) { | |||
return optionsContainer.addFocusHandler(handler); | |||
} | |||
@Override | |||
public HandlerRegistration addBlurHandler(BlurHandler handler) { | |||
return optionsContainer.addBlurHandler(handler); | |||
} | |||
} |
@@ -17,16 +17,17 @@ package com.vaadin.client.ui; | |||
import java.util.Objects; | |||
import com.google.gwt.user.client.ui.Composite; | |||
import com.google.gwt.user.client.ui.FlowPanel; | |||
import com.google.gwt.event.dom.client.HasAllFocusHandlers; | |||
import com.google.gwt.user.client.ui.ListBox; | |||
import com.vaadin.client.widgets.FocusableFlowPanelComposite; | |||
/** | |||
* The client-side widget for the {@code NativeSelect} component. | |||
* | |||
* @author Vaadin Ltd. | |||
*/ | |||
public class VNativeSelect extends Composite { | |||
public class VNativeSelect extends FocusableFlowPanelComposite | |||
implements HasAllFocusHandlers { | |||
private final ListBox listBox = new ListBox(); | |||
@@ -34,9 +35,7 @@ public class VNativeSelect extends Composite { | |||
* Creates a new {@code VNativeSelect} instance. | |||
*/ | |||
public VNativeSelect() { | |||
FlowPanel panel = new FlowPanel(); | |||
panel.add(listBox); | |||
initWidget(panel); | |||
getWidget().add(listBox); | |||
} | |||
/** |
@@ -18,7 +18,7 @@ package com.vaadin.client.ui.nativeselect; | |||
import com.google.gwt.event.shared.HandlerRegistration; | |||
import com.vaadin.client.annotations.OnStateChange; | |||
import com.vaadin.client.connectors.AbstractListingConnector; | |||
import com.vaadin.client.connectors.AbstractFocusableListingConnector; | |||
import com.vaadin.client.data.DataSource; | |||
import com.vaadin.client.ui.VNativeSelect; | |||
import com.vaadin.shared.Range; | |||
@@ -41,8 +41,8 @@ import elemental.json.JsonObject; | |||
* @since 8.0 | |||
*/ | |||
@Connect(com.vaadin.ui.NativeSelect.class) | |||
public class NativeSelectConnector | |||
extends AbstractListingConnector<SelectionModel.Single<?>> { | |||
public class NativeSelectConnector extends | |||
AbstractFocusableListingConnector<VNativeSelect, SelectionModel.Single<?>> { | |||
private HandlerRegistration selectionChangeRegistration; | |||
private Registration dataChangeRegistration; | |||
@@ -67,11 +67,6 @@ public class NativeSelectConnector | |||
selectionChangeRegistration = null; | |||
} | |||
@Override | |||
public VNativeSelect getWidget() { | |||
return (VNativeSelect) super.getWidget(); | |||
} | |||
@Override | |||
public void setDataSource(DataSource<JsonObject> dataSource) { | |||
if (dataChangeRegistration != null) { |
@@ -22,9 +22,8 @@ import java.util.HashSet; | |||
import java.util.List; | |||
import com.vaadin.client.communication.StateChangeEvent; | |||
import com.vaadin.client.connectors.AbstractListingConnector; | |||
import com.vaadin.client.connectors.AbstractFocusableListingConnector; | |||
import com.vaadin.client.data.DataSource; | |||
import com.vaadin.client.ui.ConnectorFocusAndBlurHandler; | |||
import com.vaadin.client.ui.VCheckBoxGroup; | |||
import com.vaadin.shared.data.selection.MultiSelectServerRpc; | |||
import com.vaadin.shared.data.selection.SelectionModel; | |||
@@ -36,23 +35,13 @@ import elemental.json.JsonObject; | |||
@Connect(CheckBoxGroup.class) | |||
// We don't care about the framework-provided selection model at this point | |||
public class CheckBoxGroupConnector | |||
extends AbstractListingConnector<SelectionModel<?>> { | |||
private ConnectorFocusAndBlurHandler handler; | |||
public class CheckBoxGroupConnector extends | |||
AbstractFocusableListingConnector<VCheckBoxGroup, SelectionModel<?>> { | |||
@Override | |||
protected void init() { | |||
super.init(); | |||
getWidget().addSelectionChangeHandler(this::selectionChanged); | |||
handler = ConnectorFocusAndBlurHandler.addHandlers(this); | |||
} | |||
@Override | |||
public void onUnregister() { | |||
super.onUnregister(); | |||
handler.removeHandlers(); | |||
handler = null; | |||
} | |||
private void selectionChanged(JsonObject changedItem, Boolean selected) { | |||
@@ -90,11 +79,6 @@ public class CheckBoxGroupConnector | |||
getWidget().buildOptions(items); | |||
} | |||
@Override | |||
public VCheckBoxGroup getWidget() { | |||
return (VCheckBoxGroup) super.getWidget(); | |||
} | |||
@Override | |||
public CheckBoxGroupState getState() { | |||
return (CheckBoxGroupState) super.getState(); |
@@ -0,0 +1,58 @@ | |||
/* | |||
* 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.client.widgets; | |||
import com.google.gwt.event.dom.client.BlurHandler; | |||
import com.google.gwt.event.dom.client.FocusHandler; | |||
import com.google.gwt.event.dom.client.HasAllFocusHandlers; | |||
import com.google.gwt.event.shared.HandlerRegistration; | |||
import com.google.gwt.user.client.ui.Composite; | |||
/** | |||
* Focusable composite whose widget is {@link ChildFocusAwareFlowPanel} (flow | |||
* panel that tracks focus/blur events from its children). | |||
* | |||
* @author Vaadin Ltd | |||
* | |||
*/ | |||
public abstract class FocusableFlowPanelComposite extends Composite | |||
implements HasAllFocusHandlers { | |||
private final ChildFocusAwareFlowPanel panel; | |||
/** | |||
* Creates a new instance. | |||
*/ | |||
protected FocusableFlowPanelComposite() { | |||
panel = new ChildFocusAwareFlowPanel(); | |||
initWidget(panel); | |||
} | |||
@Override | |||
protected final ChildFocusAwareFlowPanel getWidget() { | |||
return (ChildFocusAwareFlowPanel) super.getWidget(); | |||
} | |||
@Override | |||
public HandlerRegistration addFocusHandler(FocusHandler handler) { | |||
return panel.addFocusHandler(handler); | |||
} | |||
@Override | |||
public HandlerRegistration addBlurHandler(BlurHandler handler) { | |||
return panel.addBlurHandler(handler); | |||
} | |||
} |
@@ -18,7 +18,15 @@ package com.vaadin.ui; | |||
import java.util.Collection; | |||
import com.vaadin.event.FieldEvents.BlurEvent; | |||
import com.vaadin.event.FieldEvents.BlurListener; | |||
import com.vaadin.event.FieldEvents.BlurNotifier; | |||
import com.vaadin.event.FieldEvents.FocusAndBlurServerRpcDecorator; | |||
import com.vaadin.event.FieldEvents.FocusEvent; | |||
import com.vaadin.event.FieldEvents.FocusListener; | |||
import com.vaadin.event.FieldEvents.FocusNotifier; | |||
import com.vaadin.server.data.DataSource; | |||
import com.vaadin.shared.Registration; | |||
import com.vaadin.shared.data.DataCommunicatorConstants; | |||
import com.vaadin.shared.ui.nativeselect.NativeSelectState; | |||
@@ -34,14 +42,16 @@ import com.vaadin.shared.ui.nativeselect.NativeSelectState; | |||
* | |||
* @see com.vaadin.ui.ComboBox | |||
*/ | |||
public class NativeSelect<T> extends AbstractSingleSelect<T> { | |||
public class NativeSelect<T> extends AbstractSingleSelect<T> | |||
implements FocusNotifier, BlurNotifier { | |||
/** | |||
* Creates a new {@code NativeSelect} with an empty caption and no items. | |||
*/ | |||
public NativeSelect() { | |||
addDataGenerator((item, json) -> json.put( | |||
DataCommunicatorConstants.DATA, String.valueOf(item))); | |||
registerRpc(new FocusAndBlurServerRpcDecorator(this, this::fireEvent)); | |||
addDataGenerator((item, json) -> json | |||
.put(DataCommunicatorConstants.DATA, String.valueOf(item))); | |||
setSelectionModel(new SimpleSingleSelection()); | |||
} | |||
@@ -85,6 +95,34 @@ public class NativeSelect<T> extends AbstractSingleSelect<T> { | |||
setDataSource(dataSource); | |||
} | |||
@Override | |||
public Registration addFocusListener(FocusListener listener) { | |||
addListener(FocusEvent.EVENT_ID, FocusEvent.class, listener, | |||
FocusListener.focusMethod); | |||
return () -> removeListener(FocusEvent.EVENT_ID, FocusEvent.class, | |||
listener); | |||
} | |||
@Override | |||
@Deprecated | |||
public void removeFocusListener(FocusListener listener) { | |||
removeListener(FocusEvent.EVENT_ID, FocusEvent.class, listener); | |||
} | |||
@Override | |||
public Registration addBlurListener(BlurListener listener) { | |||
addListener(BlurEvent.EVENT_ID, BlurEvent.class, listener, | |||
BlurListener.blurMethod); | |||
return () -> removeListener(BlurEvent.EVENT_ID, BlurEvent.class, | |||
listener); | |||
} | |||
@Override | |||
@Deprecated | |||
public void removeBlurListener(BlurListener listener) { | |||
removeListener(BlurEvent.EVENT_ID, BlurEvent.class, listener); | |||
} | |||
@Override | |||
protected NativeSelectState getState() { | |||
return getState(true); |
@@ -0,0 +1,42 @@ | |||
/* | |||
* 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.nativeselect; | |||
import java.util.stream.Collectors; | |||
import java.util.stream.IntStream; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.tests.components.AbstractTestUIWithLog; | |||
import com.vaadin.ui.NativeSelect; | |||
/** | |||
* @author Vaadin Ltd | |||
* | |||
*/ | |||
public class NativeSelectFocusBlur extends AbstractTestUIWithLog { | |||
@Override | |||
protected void setup(VaadinRequest request) { | |||
NativeSelect<Integer> select = new NativeSelect<>(); | |||
select.setItems(IntStream.range(1, 10).mapToObj(Integer::valueOf) | |||
.collect(Collectors.toList())); | |||
addComponent(select); | |||
select.addFocusListener(event -> log("Focus Event")); | |||
select.addBlurListener(event -> log("Blur Event")); | |||
} | |||
} |
@@ -0,0 +1,63 @@ | |||
/* | |||
* 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.nativeselect; | |||
import java.util.List; | |||
import org.junit.Assert; | |||
import org.junit.Test; | |||
import org.openqa.selenium.Keys; | |||
import com.vaadin.testbench.TestBenchElement; | |||
import com.vaadin.testbench.customelements.NativeSelectElement; | |||
import com.vaadin.testbench.elements.LabelElement; | |||
import com.vaadin.tests.tb3.MultiBrowserTest; | |||
/** | |||
* @author Vaadin Ltd | |||
* | |||
*/ | |||
public class NativeSelectFocusBlurTest extends MultiBrowserTest { | |||
@Test | |||
public void focusBlurEvents() { | |||
openTestURL(); | |||
NativeSelectElement nativeSelect = $(NativeSelectElement.class).first(); | |||
nativeSelect.click(); | |||
// Focus event is fired | |||
Assert.assertTrue(logContainsText("1. Focus Event")); | |||
List<TestBenchElement> options = nativeSelect.getOptions(); | |||
options.get(1).click(); | |||
// No any new event | |||
Assert.assertFalse(logContainsText("2.")); | |||
// click on log label => blur | |||
$(LabelElement.class).first().click(); | |||
// blur event is fired | |||
Assert.assertTrue(logContainsText("2. Blur Event")); | |||
nativeSelect.click(); | |||
// Focus event is fired | |||
Assert.assertTrue(logContainsText("3. Focus Event")); | |||
options.get(1).sendKeys(Keys.ARROW_UP, Keys.ENTER); | |||
// No any new event | |||
Assert.assertFalse(logContainsText("4.")); | |||
} | |||
} |