diff options
8 files changed, 452 insertions, 6 deletions
diff --git a/client/src/main/java/com/vaadin/client/ui/VNativeSelect.java b/client/src/main/java/com/vaadin/client/ui/VNativeSelect.java new file mode 100644 index 0000000000..4a3d95c0a5 --- /dev/null +++ b/client/src/main/java/com/vaadin/client/ui/VNativeSelect.java @@ -0,0 +1,34 @@ +/* + * 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.ui; + +import com.google.gwt.user.client.ui.ListBox; +import com.vaadin.shared.ui.nativeselect.NativeSelectState; + +/** + * The client-side widget for the {@code NativeSelect} component. + * + * @author Vaadin Ltd. + */ +public class VNativeSelect extends ListBox { + + /** + * Creates a new {@code VNativeSelect} instance. + */ + public VNativeSelect() { + setStyleName(NativeSelectState.STYLE_NAME); + } +} diff --git a/client/src/main/java/com/vaadin/client/ui/nativeselect/NativeSelectConnector.java b/client/src/main/java/com/vaadin/client/ui/nativeselect/NativeSelectConnector.java new file mode 100644 index 0000000000..afa202e174 --- /dev/null +++ b/client/src/main/java/com/vaadin/client/ui/nativeselect/NativeSelectConnector.java @@ -0,0 +1,102 @@ +/* + * 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.ui.nativeselect; + +import com.vaadin.client.annotations.OnStateChange; +import com.vaadin.client.connectors.AbstractListingConnector; +import com.vaadin.client.data.DataSource; +import com.vaadin.client.ui.VNativeSelect; +import com.vaadin.shared.Range; +import com.vaadin.shared.Registration; +import com.vaadin.shared.data.selection.SelectionModel; +import com.vaadin.shared.ui.Connect; + +import elemental.json.JsonObject; + +/** + * The client-side connector for the {@code NativeSelect} component. + * + * @author Vaadin Ltd. + * + * @see com.vaadin.ui.NativeSelect + * @see com.vaadin.client.ui.VNativeSelect + * + * @since 8.0 + */ +@Connect(com.vaadin.ui.NativeSelect.class) +public class NativeSelectConnector extends + AbstractListingConnector<SelectionModel<?>> { + + private Registration dataChangeRegistration; + + @Override + public VNativeSelect getWidget() { + return (VNativeSelect) super.getWidget(); + } + + @Override + public void setDataSource(DataSource<JsonObject> dataSource) { + if (dataChangeRegistration != null) { + dataChangeRegistration.remove(); + } + dataChangeRegistration = dataSource.addDataChangeHandler( + this::onDataChange); + super.setDataSource(dataSource); + } + + @OnStateChange("readOnly") + @SuppressWarnings("deprecation") + void updateWidgetReadOnly() { + getWidget().setEnabled(isEnabled() && !isReadOnly()); + } + + /** + * A data change handler registered to the data source. Updates the data + * items and selection status when the data source notifies of new changes + * from the server side. + * + * @param range + * the new range of data items + */ + private void onDataChange(Range range) { + assert range.getStart() == 0 && range.getEnd() == getDataSource() + .size() : "NativeSelect only supports full updates, but got range " + + range; + + final VNativeSelect select = getWidget(); + final int itemCount = select.getItemCount(); + + for (int i = range.getStart(); i < range.getEnd(); i++) { + + final JsonObject row = getDataSource().getRow(i); + + if (i < itemCount) { + // Reuse and update an existing item + select.setItemText(i, getRowData(row).asString()); + select.setValue(i, getRowKey(row)); + } else { + // Add new items if the new dataset is larger than the old + select.addItem(getRowData(row).asString(), getRowKey(row)); + } + } + + for (int i = select.getItemCount() - 1; i >= range.getEnd(); i--) { + // Remove extra items if the new dataset is smaller than the old + select.removeItem(i); + } + } +} diff --git a/server/src/main/java/com/vaadin/ui/NativeSelect.java b/server/src/main/java/com/vaadin/ui/NativeSelect.java new file mode 100644 index 0000000000..9a400a1a01 --- /dev/null +++ b/server/src/main/java/com/vaadin/ui/NativeSelect.java @@ -0,0 +1,114 @@ +/* + * 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.ui; + +import java.util.Collection; +import java.util.Collections; +import java.util.Set; + +import com.vaadin.server.data.DataSource; +import com.vaadin.shared.data.DataCommunicatorConstants; +import com.vaadin.shared.data.selection.SelectionModel; +import com.vaadin.shared.ui.nativeselect.NativeSelectState; + +/** + * A simple drop-down select component. Represented on the client side by a + * "native" HTML {@code <select>} element. Lacks advanced features such as lazy + * loading, filtering, and adding new items. + * + * @author Vaadin Ltd. + * + * @param <T> + * the data item type + * + * @see com.vaadin.ui.ComboBox + */ +public class NativeSelect<T> extends AbstractListing<T, SelectionModel<T>> { + + /** + * Creates a new {@code NativeSelect} with an empty caption and no items. + */ + public NativeSelect() { + addDataGenerator((item, json) -> json.put( + DataCommunicatorConstants.DATA, String.valueOf(item))); + + setSelectionModel(new SelectionModel<T>() { + + @Override + public Set<T> getSelectedItems() { + return Collections.emptySet(); + } + + @Override + public void select(T item) { + } + + @Override + public void deselect(T item) { + } + }); + } + + /** + * Creates a new {@code NativeSelect} with the given caption and no items. + * + * @param caption + * the component caption to set, null for no caption + */ + public NativeSelect(String caption) { + this(); + setCaption(caption); + } + + /** + * Creates a new {@code NativeSelect} with the given caption, containing the + * data items in the given collection. + * + * @param caption + * the component caption to set, null for no caption + * @param items + * the data items to use, not null + */ + public NativeSelect(String caption, Collection<T> items) { + this(caption); + setItems(items); + } + + /** + * Creates a new {@code NativeSelect} with the given caption, using the + * given {@code DataSource} as the source of data items. + * + * @param caption + * the component caption to set, null for no caption + * @param dataSource + * the source of data items to use, not null + */ + public NativeSelect(String caption, DataSource<T> dataSource) { + this(caption); + setDataSource(dataSource); + } + + @Override + protected NativeSelectState getState() { + return getState(true); + } + + @Override + protected NativeSelectState getState(boolean markAsDirty) { + return (NativeSelectState) super.getState(markAsDirty); + } +} diff --git a/shared/src/main/java/com/vaadin/shared/ui/nativeselect/NativeSelectState.java b/shared/src/main/java/com/vaadin/shared/ui/nativeselect/NativeSelectState.java new file mode 100644 index 0000000000..f5a6248bfb --- /dev/null +++ b/shared/src/main/java/com/vaadin/shared/ui/nativeselect/NativeSelectState.java @@ -0,0 +1,37 @@ +/* + * 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.shared.ui.nativeselect; + +import com.vaadin.shared.ui.AbstractSingleSelectState; + +/** + * Shared state for {@code NativeSelect}. + * + * @author Vaadin Ltd. + * + * @since 8.0 + */ +public class NativeSelectState extends AbstractSingleSelectState { + + /** + * The default primary style name for {@code NativeSelect}. + */ + public static final String STYLE_NAME = "v-nativeselect"; + + { + primaryStyleName = STYLE_NAME; + } +} diff --git a/uitest-common/src/main/java/com/vaadin/testbench/customelements/NativeSelectElement.java b/uitest-common/src/main/java/com/vaadin/testbench/customelements/NativeSelectElement.java new file mode 100644 index 0000000000..e5d572eaa9 --- /dev/null +++ b/uitest-common/src/main/java/com/vaadin/testbench/customelements/NativeSelectElement.java @@ -0,0 +1,75 @@ +/* + * 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.testbench.customelements; + +import java.util.List; + +import org.openqa.selenium.support.ui.Select; + +import com.vaadin.testbench.TestBenchElement; +import com.vaadin.testbench.elements.AbstractSelectElement; +import com.vaadin.testbench.elementsbase.ServerClass; + +@ServerClass("com.vaadin.ui.NativeSelect") +public class NativeSelectElement extends AbstractSelectElement { + private Select selectElement; + + @Override + protected void init() { + super.init(); + selectElement = new Select(this); + } + + public List<TestBenchElement> getOptions() { + return wrapElements(selectElement.getOptions(), getCommandExecutor()); + } + + public void selectByText(String text) throws ReadOnlyException { + if (isReadOnly()) { + throw new ReadOnlyException(); + } + selectElement.selectByVisibleText(text); + waitForVaadin(); + } + + /** + * Clear operation is not supported for Native Select. This operation has no + * effect on Native Select element. + */ + @Override + public void clear() { + super.clear(); + } + + /** + * Return value of the selected item in the native select element + * + * @return value of the selected item in the native select element + */ + public String getValue() { + return selectElement.getFirstSelectedOption().getText(); + } + + /** + * Select item of the native select element with the specified value + * + * @param chars + * value of the native select item will be selected + */ + public void setValue(CharSequence chars) throws ReadOnlyException { + selectByText((String) chars); + } +} diff --git a/uitest/src/main/java/com/vaadin/tests/components/abstractlisting/AbstractListingTestUI.java b/uitest/src/main/java/com/vaadin/tests/components/abstractlisting/AbstractListingTestUI.java index e9fd2d0c36..6267890535 100644 --- a/uitest/src/main/java/com/vaadin/tests/components/abstractlisting/AbstractListingTestUI.java +++ b/uitest/src/main/java/com/vaadin/tests/components/abstractlisting/AbstractListingTestUI.java @@ -3,20 +3,22 @@ package com.vaadin.tests.components.abstractlisting; import java.util.LinkedHashMap; import java.util.stream.IntStream; +import com.vaadin.annotations.Widgetset; import com.vaadin.tests.components.AbstractComponentTest; import com.vaadin.ui.AbstractListing; -public abstract class AbstractListingTestUI<T extends AbstractListing<Object, ?>, V> +@Widgetset("com.vaadin.DefaultWidgetSet") +public abstract class AbstractListingTestUI<T extends AbstractListing<Object, ?>> extends AbstractComponentTest<T> { @Override protected void createActions() { super.createActions(); - createItemsSelect(); - createSelectionSelect(); + createItemsMenu(); + createSelectionMenu(); } - protected void createItemsSelect() { + protected void createItemsMenu() { LinkedHashMap<String, Integer> options = new LinkedHashMap<>(); for (int i = 0; i <= 10; i++) { options.put(String.valueOf(i), i); @@ -33,7 +35,7 @@ public abstract class AbstractListingTestUI<T extends AbstractListing<Object, ?> }); } - protected void createSelectionSelect() { + protected void createSelectionMenu() { LinkedHashMap<String, String> options = new LinkedHashMap<>(); options.put("None", null); options.put("Item 0", "Item 0"); @@ -53,7 +55,7 @@ public abstract class AbstractListingTestUI<T extends AbstractListing<Object, ?> } protected Object[] createItems(int number) { - return IntStream.rangeClosed(0, number) + return IntStream.range(0, number) .mapToObj(i -> "Item " + i) .toArray(); } diff --git a/uitest/src/main/java/com/vaadin/tests/components/nativeselect/NativeSelects.java b/uitest/src/main/java/com/vaadin/tests/components/nativeselect/NativeSelects.java new file mode 100644 index 0000000000..3338f8156f --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/nativeselect/NativeSelects.java @@ -0,0 +1,14 @@ +package com.vaadin.tests.components.nativeselect; + +import com.vaadin.tests.components.abstractlisting.AbstractListingTestUI; +import com.vaadin.ui.NativeSelect; + +public class NativeSelects extends + AbstractListingTestUI<NativeSelect<Object>> { + + @Override + @SuppressWarnings({ "unchecked", "rawtypes" }) + protected Class<NativeSelect<Object>> getTestClass() { + return (Class) NativeSelect.class; + } +} diff --git a/uitest/src/test/java/com/vaadin/tests/components/nativeselect/NativeSelectTest.java b/uitest/src/test/java/com/vaadin/tests/components/nativeselect/NativeSelectTest.java new file mode 100644 index 0000000000..ae971630ad --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/nativeselect/NativeSelectTest.java @@ -0,0 +1,68 @@ +/* + * 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 static org.junit.Assert.assertEquals; + +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.testbench.TestBenchElement; +import com.vaadin.testbench.customelements.NativeSelectElement; +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class NativeSelectTest extends MultiBrowserTest { + + @Before + public void setUp() { + openTestURL(); + } + + @Test + public void initialLoad_containsCorrectItems() { + assertItems(20); + } + + @Test + public void initialItems_reduceItemCount_containsCorrectItems() { + selectMenuPath("Component", "Data source", "Items", "5"); + assertItems(5); + } + + @Test + public void initialItems_increaseItemCount_containsCorrectItems() { + selectMenuPath("Component", "Data source", "Items", "100"); + assertItems(100); + } + + @Override + protected Class<?> getUIClass() { + return NativeSelects.class; + } + + protected NativeSelectElement getSelect() { + return $(NativeSelectElement.class).first(); + } + + protected void assertItems(int count) { + int i = 0; + for (TestBenchElement e : getSelect().getOptions()) { + assertEquals("Item " + i, e.getText()); + i++; + } + assertEquals("Number of items", count, i); + } +} |