aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTeemu Suo-Anttila <teemusa@vaadin.com>2016-02-03 14:19:25 +0200
committerVaadin Code Review <review@vaadin.com>2016-03-05 12:02:08 +0000
commitacab3d3c0c8eda303a8aa331ba5b17d38164542a (patch)
treec9690995d71c6efe71f58f4b37ed01e07f9c0582
parenteca0b72f697b3e79690c2dc02014ad7953edd6cd (diff)
downloadvaadin-framework-acab3d3c0c8eda303a8aa331ba5b17d38164542a.tar.gz
vaadin-framework-acab3d3c0c8eda303a8aa331ba5b17d38164542a.zip
Add simple ListBox component to use with the DataProvider
This patch changes the DummyDataProvider test to use the ListBox storing runnables instead of Buttons with actions. Change-Id: Ie3831ccbd397eb2c145cf2c036fa5e512dcfa230
-rw-r--r--client/src/com/vaadin/client/connectors/proto/ListBoxConnector.java141
-rw-r--r--server/src/com/vaadin/ui/proto/ListBox.java145
-rw-r--r--shared/src/com/vaadin/shared/ui/proto/listbox/ListBoxConstants.java27
-rw-r--r--shared/src/com/vaadin/shared/ui/proto/listbox/ListBoxSelectRpc.java29
-rw-r--r--uitest/src/com/vaadin/tests/dataprovider/DummyDataProviderTest.java31
-rw-r--r--uitest/src/com/vaadin/tests/dataprovider/DummyDataProviderUI.java64
6 files changed, 415 insertions, 22 deletions
diff --git a/client/src/com/vaadin/client/connectors/proto/ListBoxConnector.java b/client/src/com/vaadin/client/connectors/proto/ListBoxConnector.java
new file mode 100644
index 0000000000..d5857b2571
--- /dev/null
+++ b/client/src/com/vaadin/client/connectors/proto/ListBoxConnector.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2000-2014 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.proto;
+
+import com.google.gwt.event.dom.client.ChangeEvent;
+import com.google.gwt.event.dom.client.ChangeHandler;
+import com.google.gwt.user.client.ui.ListBox;
+import com.vaadin.client.data.DataChangeHandler;
+import com.vaadin.client.data.DataSource;
+import com.vaadin.client.data.HasDataSource;
+import com.vaadin.client.ui.AbstractComponentConnector;
+import com.vaadin.shared.ui.Connect;
+import com.vaadin.shared.ui.proto.listbox.ListBoxConstants;
+import com.vaadin.shared.ui.proto.listbox.ListBoxSelectRpc;
+
+import elemental.json.JsonObject;
+
+@Connect(com.vaadin.ui.proto.ListBox.class)
+public class ListBoxConnector extends AbstractComponentConnector implements
+ HasDataSource {
+
+ private DataSource<JsonObject> dataSource;
+ private ListBoxSelectRpc rpc;
+
+ @Override
+ public ListBox getWidget() {
+ return (ListBox) super.getWidget();
+ }
+
+ @Override
+ protected void init() {
+ super.init();
+
+ rpc = getRpcProxy(ListBoxSelectRpc.class);
+ getWidget().addChangeHandler(new ChangeHandler() {
+
+ @Override
+ public void onChange(ChangeEvent event) {
+ // Send selection to server
+ rpc.select(getWidget().getSelectedValue());
+ }
+ });
+
+ // TODO: Add some way to handle empty selection naming.
+ getWidget().addItem(" ", "");
+ getWidget().setSelectedIndex(0);
+ }
+
+ @Override
+ public void setDataSource(DataSource<JsonObject> rpcDataSource) {
+ // Clean up old data source
+ if (dataSource != null) {
+ dataSource.setDataChangeHandler(null);
+ }
+
+ dataSource = rpcDataSource;
+ dataSource.setDataChangeHandler(new DataChangeHandler() {
+
+ @Override
+ public void resetDataAndSize(int estimatedNewDataSize) {
+ recreateOptions();
+ }
+
+ @Override
+ public void dataUpdated(int firstRowIndex, int numberOfRows) {
+ recreateOptions();
+ }
+
+ @Override
+ public void dataRemoved(int firstRowIndex, int numberOfRows) {
+ boolean resetSelection = false;
+
+ // Offset by 1, due to empty item.
+ int removedIndex = firstRowIndex + 1;
+
+ for (int i = 0; i < numberOfRows
+ && removedIndex < getWidget().getItemCount(); ++i) {
+ if (!resetSelection
+ && getWidget().getSelectedIndex() == removedIndex) {
+ resetSelection = true;
+ }
+ getWidget().removeItem(removedIndex);
+ }
+
+ if (resetSelection) {
+ getWidget().setSelectedIndex(0);
+ // No event from setSelectedIndex, call manually.
+ rpc.select("");
+ }
+ }
+
+ @Override
+ public void dataAvailable(int firstRowIndex, int numberOfRows) {
+ recreateOptions();
+ }
+
+ @Override
+ public void dataAdded(int firstRowIndex, int numberOfRows) {
+ // Add new ones to end of list, no matter the actual place
+ for (int i = 0; i < numberOfRows; ++i) {
+ addItem(firstRowIndex + i);
+ }
+ }
+ });
+ }
+
+ private void recreateOptions() {
+ // Remove all non-empty items.
+ while (getWidget().getItemCount() > 1) {
+ getWidget().removeItem(1);
+ }
+
+ for (int i = 0; i < dataSource.size(); ++i) {
+ addItem(i);
+ }
+ getWidget().setSelectedIndex(0);
+ }
+
+ protected void addItem(int index) {
+ JsonObject item = dataSource.getRow(index);
+ if (item != null) {
+ // We wrote the name provider output to "n"
+ // Key is always stored at "k"
+ getWidget().addItem(item.getString(ListBoxConstants.NAME_KEY),
+ item.getString("k"));
+ }
+ }
+} \ No newline at end of file
diff --git a/server/src/com/vaadin/ui/proto/ListBox.java b/server/src/com/vaadin/ui/proto/ListBox.java
new file mode 100644
index 0000000000..bc952c1617
--- /dev/null
+++ b/server/src/com/vaadin/ui/proto/ListBox.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2000-2014 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.ui.proto;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import com.vaadin.server.communication.data.typed.CollectionDataSource;
+import com.vaadin.server.communication.data.typed.DataProvider;
+import com.vaadin.server.communication.data.typed.DataSource;
+import com.vaadin.server.communication.data.typed.TypedDataGenerator;
+import com.vaadin.shared.ui.proto.listbox.ListBoxConstants;
+import com.vaadin.shared.ui.proto.listbox.ListBoxSelectRpc;
+import com.vaadin.ui.AbstractComponent;
+
+import elemental.json.JsonObject;
+
+/**
+ * ListBox is a simple select component typed to a data type.
+ *
+ * @since
+ */
+public class ListBox<T> extends AbstractComponent {
+
+ private static class DefaultNameProvider<T> implements NameProvider<T> {
+ @Override
+ public String getName(T value) {
+ return value.toString();
+ }
+ }
+
+ public interface NameProvider<T> extends Serializable {
+ String getName(T value);
+ }
+
+ public interface ValueChange<T> extends Serializable {
+ void valueChange(T value);
+ }
+
+ private T selected;
+ private DataProvider<T> dataProvider;
+ private Set<ValueChange<T>> listeners = new LinkedHashSet<ValueChange<T>>();
+
+ public ListBox(Collection<T> data) {
+ this(new CollectionDataSource<T>(data), new DefaultNameProvider<T>());
+ }
+
+ public ListBox(DataSource<T> data) {
+ this(data, new DefaultNameProvider<T>());
+ }
+
+ public ListBox(Collection<T> data, NameProvider<T> nameProvider) {
+ this(new CollectionDataSource<T>(data), nameProvider);
+ }
+
+ public ListBox(DataSource<T> data, NameProvider<T> nameProvider) {
+ setDataSource(data, nameProvider);
+ registerRpc(new ListBoxSelectRpc() {
+
+ @Override
+ public void select(String key) {
+ if (key != null && !key.isEmpty()) {
+ // Key mapper gives the object represented by the given
+ // key communicated to the client-side
+ selected = dataProvider.getKeyMapper().get(key);
+ fireSelectionChange(selected);
+ } else {
+ selected = null;
+ }
+ }
+ });
+ }
+
+ public ListBox() {
+ this(new CollectionDataSource<T>(new ArrayList<T>()),
+ new DefaultNameProvider<T>());
+
+ }
+
+ public void setDataSource(Collection<T> data) {
+ setDataSource(data, new DefaultNameProvider<T>());
+ }
+
+ public void setDataSource(DataSource<T> data) {
+ setDataSource(data, new DefaultNameProvider<T>());
+ }
+
+ public void setDataSource(Collection<T> data, NameProvider<T> nameProvider) {
+ setDataSource(new CollectionDataSource<T>(data), nameProvider);
+ }
+
+ public void setDataSource(DataSource<T> data,
+ final NameProvider<T> nameProvider) {
+ dataProvider = DataProvider.create(data, this);
+ dataProvider.addDataGenerator(new TypedDataGenerator<T>() {
+
+ @Override
+ public void generateData(T bean, JsonObject rowData) {
+ rowData.put(ListBoxConstants.NAME_KEY,
+ nameProvider.getName(bean));
+ }
+
+ @Override
+ public void destroyData(T bean) {
+ // No data needs to be destroyed.
+ }
+ });
+ }
+
+ public void addValueChangeListener(ValueChange<T> listener) {
+ listeners.add(listener);
+ }
+
+ public void removeValueChangeListener(ValueChange<T> listener) {
+ listeners.remove(listener);
+ }
+
+ public T getSelected() {
+ return selected;
+ }
+
+ protected void fireSelectionChange(T selected) {
+ List<ValueChange<T>> set = new ArrayList<ValueChange<T>>(listeners);
+ for (ValueChange<T> listener : set) {
+ listener.valueChange(selected);
+ }
+ }
+}
diff --git a/shared/src/com/vaadin/shared/ui/proto/listbox/ListBoxConstants.java b/shared/src/com/vaadin/shared/ui/proto/listbox/ListBoxConstants.java
new file mode 100644
index 0000000000..3f6ef75683
--- /dev/null
+++ b/shared/src/com/vaadin/shared/ui/proto/listbox/ListBoxConstants.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2000-2014 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.shared.ui.proto.listbox;
+
+import java.io.Serializable;
+
+/**
+ * Constants used in ListBox communication.
+ *
+ * @since
+ */
+public final class ListBoxConstants implements Serializable {
+ public static final String NAME_KEY = "n";
+}
diff --git a/shared/src/com/vaadin/shared/ui/proto/listbox/ListBoxSelectRpc.java b/shared/src/com/vaadin/shared/ui/proto/listbox/ListBoxSelectRpc.java
new file mode 100644
index 0000000000..fa37463cea
--- /dev/null
+++ b/shared/src/com/vaadin/shared/ui/proto/listbox/ListBoxSelectRpc.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2000-2014 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.shared.ui.proto.listbox;
+
+import com.vaadin.shared.communication.ServerRpc;
+
+/**
+ * Simple client to server rpc to inform ListBox component about selection.
+ *
+ * @since
+ */
+public interface ListBoxSelectRpc extends ServerRpc {
+
+ void select(String key);
+
+} \ No newline at end of file
diff --git a/uitest/src/com/vaadin/tests/dataprovider/DummyDataProviderTest.java b/uitest/src/com/vaadin/tests/dataprovider/DummyDataProviderTest.java
index 3821428ab2..9396dc05f0 100644
--- a/uitest/src/com/vaadin/tests/dataprovider/DummyDataProviderTest.java
+++ b/uitest/src/com/vaadin/tests/dataprovider/DummyDataProviderTest.java
@@ -29,9 +29,12 @@ import java.util.Random;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.ui.Select;
import com.vaadin.shared.data.DataProviderConstants;
+import com.vaadin.testbench.elements.AbstractComponentElement;
import com.vaadin.testbench.elements.ButtonElement;
+import com.vaadin.testbench.elementsbase.ServerClass;
import com.vaadin.tests.fieldgroup.ComplexPerson;
import com.vaadin.tests.tb3.SingleBrowserTest;
@@ -40,6 +43,14 @@ import elemental.json.JsonObject;
public class DummyDataProviderTest extends SingleBrowserTest {
+ @ServerClass("com.vaadin.ui.proto.ListBox")
+ public static class ListBoxElement extends AbstractComponentElement {
+
+ public void selectByText(String text) {
+ new Select(this).selectByVisibleText(text);
+ }
+ }
+
// Each test uses a set of person objects (generated json) that is supposed
// to match the data sent to the client-side.
private List<JsonObject> personObjects = new ArrayList<JsonObject>();
@@ -113,10 +124,12 @@ public class DummyDataProviderTest extends SingleBrowserTest {
openTestURL();
- $(ButtonElement.class).id("sort").click();
+ $(ListBoxElement.class).first().selectByText("sort");
+ ButtonElement button = $(ButtonElement.class).first();
+ button.click();
// Second sort would show if any keys got destroyed/recreated.
- $(ButtonElement.class).id("sort").click();
+ button.click();
int size = DummyDataProviderUI.PERSON_COUNT + 1;
List<WebElement> labels = findElements(By.className("v-label"));
@@ -143,13 +156,16 @@ public class DummyDataProviderTest extends SingleBrowserTest {
openTestURL();
- $(ButtonElement.class).id("sort").click();
+ $(ListBoxElement.class).first().selectByText("sort");
+ ButtonElement button = $(ButtonElement.class).first();
+ button.click();
String text = findElements(By.className("v-label")).get(3).getText();
String json = personObjects.get(2).toJson();
assertEquals("Data not sorted", json, text);
- $(ButtonElement.class).id("remove").click();
+ $(ListBoxElement.class).first().selectByText("remove");
+ button.click();
text = findElements(By.className("v-label")).get(3).getText();
json = personObjects.get(3).toJson();
@@ -169,7 +185,9 @@ public class DummyDataProviderTest extends SingleBrowserTest {
String text = findElements(By.className("v-label")).get(1).getText();
assertEquals("Initial data did not match", json, text);
- $(ButtonElement.class).id("edit").click();
+ $(ListBoxElement.class).first().selectByText("edit");
+ ButtonElement button = $(ButtonElement.class).first();
+ button.click();
persons.get(0).setFirstName("Foo");
createPersonObjects();
@@ -182,7 +200,8 @@ public class DummyDataProviderTest extends SingleBrowserTest {
text = findElements(By.className("v-label")).get(1).getText();
assertEquals("Modified data did not match", json, text);
- $(ButtonElement.class).id("edit").click();
+ $(ListBoxElement.class).first().selectByText("edit");
+ button.click();
text = findElements(By.className("v-label")).get(1).getText();
assertEquals("Running edit again shouldn't change anything", json, text);
diff --git a/uitest/src/com/vaadin/tests/dataprovider/DummyDataProviderUI.java b/uitest/src/com/vaadin/tests/dataprovider/DummyDataProviderUI.java
index 0cd45f1994..984035a5d1 100644
--- a/uitest/src/com/vaadin/tests/dataprovider/DummyDataProviderUI.java
+++ b/uitest/src/com/vaadin/tests/dataprovider/DummyDataProviderUI.java
@@ -15,10 +15,12 @@
*/
package com.vaadin.tests.dataprovider;
+import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Random;
@@ -37,12 +39,27 @@ import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.HorizontalLayout;
+import com.vaadin.ui.proto.ListBox;
+import com.vaadin.ui.proto.ListBox.NameProvider;
import elemental.json.JsonObject;
@Widgetset(TestingWidgetSet.NAME)
public class DummyDataProviderUI extends AbstractTestUI {
+ abstract static class MyRunnable implements Runnable, Serializable {
+
+ private final String name;
+
+ public MyRunnable(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+
public static class DummyDataComponent extends AbstractComponent {
private SimpleDataProvider<ComplexPerson> dataProvider;
@@ -99,50 +116,65 @@ public class DummyDataProviderUI extends AbstractTestUI {
private Random r = new Random(RANDOM_SEED);
private MyDataSource dataSource;
private DummyDataComponent dummy;
+ private ListBox<MyRunnable> listBox;
+ private List<ComplexPerson> persons;
@Override
protected void setup(VaadinRequest request) {
- dataSource = new MyDataSource(createPersons(PERSON_COUNT, r));
+
+ persons = createPersons(PERSON_COUNT, r);
+ dataSource = new MyDataSource(persons);
dummy = new DummyDataComponent(dataSource);
- Button remove = new Button("Remove third", new ClickListener() {
+ Collection<MyRunnable> actions = new LinkedHashSet<MyRunnable>();
+ actions.add(new MyRunnable("remove") {
@Override
- public void buttonClick(ClickEvent event) {
+ public void run() {
dataSource.remove(dataSource.getData().get(2));
}
});
- Button add = new Button("Add new", new ClickListener() {
+ actions.add(new MyRunnable("add") {
@Override
- public void buttonClick(ClickEvent event) {
+ public void run() {
dataSource.save(ComplexPerson.create(r));
}
});
- Button sort = new Button("Sort content", new ClickListener() {
+ actions.add(new MyRunnable("sort") {
@Override
- public void buttonClick(ClickEvent event) {
+ public void run() {
dataSource.sort(nameComparator);
}
});
- Button edit = new Button("Edit first", new ClickListener() {
+ actions.add(new MyRunnable("edit") {
@Override
- public void buttonClick(ClickEvent event) {
- ComplexPerson p = dataSource.iterator().next();
+ public void run() {
+ ComplexPerson p = persons.get(0);
p.setFirstName("Foo");
dataSource.save(p);
}
});
- // Button Ids
- remove.setId("remove");
- add.setId("add");
- sort.setId("sort");
- edit.setId("edit");
+ listBox = new ListBox<MyRunnable>(actions,
+ new NameProvider<MyRunnable>() {
+
+ @Override
+ public String getName(MyRunnable value) {
+ return value.getName();
+ }
+ });
+
+ Button execute = new Button("Execute", new ClickListener() {
- addComponent(new HorizontalLayout(add, remove, sort, edit));
+ @Override
+ public void buttonClick(ClickEvent event) {
+ listBox.getSelected().run();
+ }
+ });
+ addComponent(new HorizontalLayout(listBox, execute));
addComponent(dummy);
}