Selection and focus/blur support not yet implemented.
Change-Id: I76752084442216e60055d93367475c1c0a612787
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
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);
});
}
- protected void createSelectionSelect() {
+ protected void createSelectionMenu() {
LinkedHashMap<String, String> options = new LinkedHashMap<>();
options.put("None", null);
options.put("Item 0", "Item 0");
}
protected Object[] createItems(int number) {
- return IntStream.rangeClosed(0, number)
+ return IntStream.range(0, number)
.mapToObj(i -> "Item " + i)
.toArray();
}
--- /dev/null
+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;
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}