瀏覽代碼

Implement focus/blur listeners for NativeSelect.

Fixes vaadin/framework8-issues#332

Change-Id: I19996ea83ed1fbe2b115d92d6be5e6a5e158f283
tags/8.0.0.alpha6
Denis Anisimov 7 年之前
父節點
當前提交
25013128a7

+ 56
- 0
client/src/main/java/com/vaadin/client/connectors/AbstractFocusableListingConnector.java 查看文件

@@ -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();
}
}

+ 10
- 34
client/src/main/java/com/vaadin/client/ui/VCheckBoxGroup.java 查看文件

@@ -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);
}
}

+ 5
- 6
client/src/main/java/com/vaadin/client/ui/VNativeSelect.java 查看文件

@@ -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);
}

/**

+ 3
- 8
client/src/main/java/com/vaadin/client/ui/nativeselect/NativeSelectConnector.java 查看文件

@@ -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) {

+ 3
- 19
client/src/main/java/com/vaadin/client/ui/optiongroup/CheckBoxGroupConnector.java 查看文件

@@ -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();

+ 58
- 0
client/src/main/java/com/vaadin/client/widgets/FocusableFlowPanelComposite.java 查看文件

@@ -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);
}
}

+ 41
- 3
server/src/main/java/com/vaadin/ui/NativeSelect.java 查看文件

@@ -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);

+ 42
- 0
uitest/src/main/java/com/vaadin/tests/components/nativeselect/NativeSelectFocusBlur.java 查看文件

@@ -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"));
}

}

+ 63
- 0
uitest/src/test/java/com/vaadin/tests/components/nativeselect/NativeSelectFocusBlurTest.java 查看文件

@@ -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."));
}
}

Loading…
取消
儲存