diff options
10 files changed, 919 insertions, 2 deletions
diff --git a/client/src/main/java/com/vaadin/client/connectors/grid/ColumnConnector.java b/client/src/main/java/com/vaadin/client/connectors/grid/ColumnConnector.java new file mode 100644 index 0000000000..4b61b62dc7 --- /dev/null +++ b/client/src/main/java/com/vaadin/client/connectors/grid/ColumnConnector.java @@ -0,0 +1,85 @@ +/* + * 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.grid; + +import com.vaadin.client.ServerConnector; +import com.vaadin.client.annotations.OnStateChange; +import com.vaadin.client.extensions.AbstractExtensionConnector; +import com.vaadin.client.widgets.Grid.Column; +import com.vaadin.shared.data.DataCommunicatorConstants; +import com.vaadin.shared.ui.Connect; +import com.vaadin.shared.ui.grid.ColumnState; + +import elemental.json.JsonObject; +import elemental.json.JsonValue; + +/** + * A connector class for columns of the Grid component. + * + * @author Vaadin Ltd + * @since + */ +@Connect(com.vaadin.ui.Grid.Column.class) +public class ColumnConnector extends AbstractExtensionConnector { + + private Column<JsonValue, JsonObject> column; + + /* This parent is needed because it's no longer available in onUnregister */ + private GridConnector parent; + + @Override + protected void extend(ServerConnector target) { + parent = getParent(); + column = new Column<JsonValue, JsonObject>() { + + @Override + public JsonValue getValue(JsonObject row) { + return row.getObject(DataCommunicatorConstants.DATA) + .get(getState().id); + } + }; + getParent().addColumn(column, getState().id); + } + + @OnStateChange("caption") + void updateCaption() { + column.setHeaderCaption(getState().caption); + } + + @OnStateChange("sortable") + void updateSortable() { + column.setSortable(getState().sortable); + } + + @Override + public void onUnregister() { + super.onUnregister(); + + parent.removeColumn(column); + column = null; + } + + @Override + public GridConnector getParent() { + return (GridConnector) super.getParent(); + } + + @Override + public ColumnState getState() { + return (ColumnState) super.getState(); + } + +} diff --git a/client/src/main/java/com/vaadin/client/connectors/grid/GridConnector.java b/client/src/main/java/com/vaadin/client/connectors/grid/GridConnector.java new file mode 100644 index 0000000000..6c809f731c --- /dev/null +++ b/client/src/main/java/com/vaadin/client/connectors/grid/GridConnector.java @@ -0,0 +1,136 @@ +/* + * 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.grid; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.vaadin.client.DeferredWorker; +import com.vaadin.client.connectors.AbstractListingConnector; +import com.vaadin.client.data.DataSource; +import com.vaadin.client.ui.SimpleManagedLayout; +import com.vaadin.client.widget.grid.selection.ClickSelectHandler; +import com.vaadin.client.widget.grid.sort.SortEvent; +import com.vaadin.client.widget.grid.sort.SortOrder; +import com.vaadin.client.widgets.Grid; +import com.vaadin.client.widgets.Grid.Column; +import com.vaadin.shared.data.sort.SortDirection; +import com.vaadin.shared.ui.Connect; +import com.vaadin.shared.ui.grid.GridServerRpc; + +import elemental.json.JsonObject; + +/** + * A connector class for the typed Grid component. + * + * @author Vaadin Ltd + * @since + */ +@Connect(com.vaadin.ui.Grid.class) +public class GridConnector extends AbstractListingConnector + implements SimpleManagedLayout, DeferredWorker { + /* Map to keep track of all added columns */ + private Map<Column<?, JsonObject>, String> columnToIdMap = new HashMap<>(); + + @Override + public Grid<JsonObject> getWidget() { + return (Grid<JsonObject>) super.getWidget(); + } + + @Override + protected void init() { + super.init(); + + new ClickSelectHandler<JsonObject>(getWidget()); + getWidget().addSortHandler(this::handleSortEvent); + + layout(); + } + + @Override + public void setDataSource(DataSource<JsonObject> dataSource) { + getWidget().setDataSource(dataSource); + } + + /** + * Adds a column to the Grid widget. For each column a communication id + * stored for client to server communication. + * + * @param column + * column to add + * @param id + * communication id + */ + public void addColumn(Column<?, JsonObject> column, String id) { + assert !columnToIdMap.containsKey(column) && !columnToIdMap + .containsValue(id) : "Column with given id already exists."; + getWidget().addColumn(column); + columnToIdMap.put(column, id); + } + + /** + * Removes a column from Grid widget. This method also removes communication + * id mapping for the column. + * + * @param column + * column to remove + */ + public void removeColumn(Column<?, JsonObject> column) { + assert columnToIdMap + .containsKey(column) : "Given Column does not exist."; + getWidget().removeColumn(column); + columnToIdMap.remove(column); + } + + @Override + public void onUnregister() { + super.onUnregister(); + + columnToIdMap.clear(); + } + + @Override + public boolean isWorkPending() { + return getWidget().isWorkPending(); + } + + @Override + public void layout() { + getWidget().onResize(); + } + + /** + * Sends sort information from an event to the server-side of the Grid. + * + * @param event + * the sort event + */ + private void handleSortEvent(SortEvent<JsonObject> event) { + List<String> columnIds = new ArrayList<>(); + List<SortDirection> sortDirections = new ArrayList<>(); + for (SortOrder so : event.getOrder()) { + if (columnToIdMap.containsKey(so.getColumn())) { + columnIds.add(columnToIdMap.get(so.getColumn())); + sortDirections.add(so.getDirection()); + } + } + getRpcProxy(GridServerRpc.class).sort(columnIds.toArray(new String[0]), + sortDirections.toArray(new SortDirection[0]), + event.isUserOriginated()); + } +} diff --git a/server/src/main/java/com/vaadin/ui/AbstractListing.java b/server/src/main/java/com/vaadin/ui/AbstractListing.java index 64d55926d5..05f31d7b81 100644 --- a/server/src/main/java/com/vaadin/ui/AbstractListing.java +++ b/server/src/main/java/com/vaadin/ui/AbstractListing.java @@ -21,7 +21,7 @@ import com.vaadin.server.data.DataSource; import com.vaadin.server.data.TypedDataGenerator; /** - * Base class for Listing components. Provides common handling for + * Base class for {@link Listing} components. Provides common handling for * {@link DataCommunicator} and {@link TypedDataGenerator}s. * * @param <T> @@ -31,7 +31,31 @@ public abstract class AbstractListing<T> extends AbstractComponent implements Listing<T> { /* DataCommunicator for this Listing component */ - private final DataCommunicator<T> dataCommunicator = new DataCommunicator<>(); + private final DataCommunicator<T> dataCommunicator; + + /** + * Constructs an {@link AbstractListing}, extending it with a + * {@link DataCommunicator}. + */ + protected AbstractListing() { + this(new DataCommunicator<>()); + } + + /** + * Constructs an {@link AbstractListing}, extending it with given + * {@link DataCommunicator}. + * <p> + * <strong>Note:</strong> This method is for creating an + * {@link AbstractListing} with a custom {@link DataCommunicator}. In the + * common case {@link AbstractListing#AbstractListing()} should be used. + * + * @param dataCommunicator + * a customized data communicator instance + */ + protected AbstractListing(DataCommunicator<T> dataCommunicator) { + this.dataCommunicator = dataCommunicator; + addExtension(dataCommunicator); + } @Override public void setDataSource(DataSource<T> dataSource) { diff --git a/server/src/main/java/com/vaadin/ui/Grid.java b/server/src/main/java/com/vaadin/ui/Grid.java new file mode 100644 index 0000000000..553d5e0761 --- /dev/null +++ b/server/src/main/java/com/vaadin/ui/Grid.java @@ -0,0 +1,408 @@ +/* + * 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.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Stream; + +import com.vaadin.server.AbstractExtension; +import com.vaadin.server.KeyMapper; +import com.vaadin.server.data.DataSource; +import com.vaadin.server.data.SortOrder; +import com.vaadin.server.data.TypedDataGenerator; +import com.vaadin.shared.MouseEventDetails; +import com.vaadin.shared.data.DataCommunicatorConstants; +import com.vaadin.shared.data.sort.SortDirection; +import com.vaadin.shared.ui.grid.ColumnState; +import com.vaadin.shared.ui.grid.GridConstants.Section; +import com.vaadin.shared.ui.grid.GridServerRpc; + +import elemental.json.Json; +import elemental.json.JsonObject; + +/** + * A grid component for displaying tabular data. + * + * @author Vaadin Ltd + * @since + * + * @param <T> + * the grid bean type + */ +public class Grid<T> extends AbstractListing<T> { + + private final class GridServerRpcImpl implements GridServerRpc { + @Override + public void sort(String[] columnIds, SortDirection[] directions, + boolean isUserOriginated) { + assert columnIds.length == directions.length : "Column and sort direction counts don't match."; + sortOrder.clear(); + if (columnIds.length == 0) { + // Grid is not sorted anymore. + getDataCommunicator() + .setBackEndSorting(Collections.emptyList()); + getDataCommunicator().setInMemorySorting(null); + return; + } + + for (int i = 0; i < columnIds.length; ++i) { + Column<T, ?> column = columnKeys.get(columnIds[i]); + sortOrder.add(new SortOrder<>(column, directions[i])); + } + + // Set sort orders + // In-memory comparator + Comparator<T> comparator = sortOrder.stream() + .map(order -> order.getSorted() + .getComparator(order.getDirection())) + .reduce((x, y) -> 0, Comparator::thenComparing); + getDataCommunicator().setInMemorySorting(comparator); + + // Back-end sort properties + List<SortOrder<String>> sortProperties = new ArrayList<>(); + sortOrder.stream() + .map(order -> order.getSorted() + .getSortOrder(order.getDirection())) + .forEach(s -> s.forEach(sortProperties::add)); + getDataCommunicator().setBackEndSorting(sortProperties); + } + + @Override + public void itemClick(String rowKey, String columnId, + MouseEventDetails details) { + // TODO Auto-generated method stub + } + + @Override + public void contextClick(int rowIndex, String rowKey, String columnId, + Section section, MouseEventDetails details) { + // TODO Auto-generated method stub + } + + @Override + public void columnsReordered(List<String> newColumnOrder, + List<String> oldColumnOrder) { + // TODO Auto-generated method stub + } + + @Override + public void columnVisibilityChanged(String id, boolean hidden, + boolean userOriginated) { + // TODO Auto-generated method stub + } + + @Override + public void columnResized(String id, double pixels) { + // TODO Auto-generated method stub + } + } + + /** + * This extension manages the configuration and data communication for a + * Column inside of a Grid component. + * + * @param <T> + * the grid bean type + * @param <V> + * the column value type + */ + public static class Column<T, V> extends AbstractExtension + implements TypedDataGenerator<T> { + + private Function<T, V> valueProvider; + private Function<SortDirection, Stream<SortOrder<String>>> sortOrderProvider; + private Comparator<T> comparator; + + /** + * Constructs a new Column configuration with given header caption and + * value provider. + * + * @param caption + * the header caption + * @param valueType + * the type of value + * @param valueProvider + * the function to get values from data objects + */ + protected Column(String caption, Class<V> valueType, + Function<T, V> valueProvider) { + Objects.requireNonNull(caption, "Header caption can't be null"); + Objects.requireNonNull(valueProvider, + "Value provider can't be null"); + Objects.requireNonNull(valueType, "Value type can't be null"); + + this.valueProvider = valueProvider; + getState().caption = caption; + sortOrderProvider = d -> Stream.of(); + + if (Comparable.class.isAssignableFrom(valueType)) { + comparator = (a, b) -> { + Comparable<V> comp = (Comparable<V>) valueProvider.apply(a); + return comp.compareTo(valueProvider.apply(b)); + }; + getState().sortable = true; + } else { + getState().sortable = false; + } + } + + @Override + public void generateData(T data, JsonObject jsonObject) { + assert getState( + false).id != null : "No communication ID set for column " + + getState(false).caption; + + if (!jsonObject.hasKey(DataCommunicatorConstants.DATA)) { + jsonObject.put(DataCommunicatorConstants.DATA, + Json.createObject()); + } + JsonObject obj = jsonObject + .getObject(DataCommunicatorConstants.DATA); + // Since we dont' have renderers yet, use a dummy toString for data. + obj.put(getState(false).id, valueProvider.apply(data).toString()); + } + + @Override + public void destroyData(T data) { + } + + @Override + protected ColumnState getState() { + return getState(true); + } + + @Override + protected ColumnState getState(boolean markAsDirty) { + return (ColumnState) super.getState(markAsDirty); + } + + /** + * This method extends the given Grid with this Column. + * + * @param grid + * the grid to extend + */ + private void extend(Grid<T> grid) { + super.extend(grid); + } + + /** + * Sets the identifier to use with this Column in communication. + * + * @param id + * the identifier string + */ + private void setId(String id) { + Objects.requireNonNull(id, "Communication ID can't be null"); + getState().id = id; + } + + /** + * Sets whether the user can sort this column or not. + * + * @param sortable + * {@code true} if the column can be sorted by the user; + * {@code false} if not + * @return this column + */ + public Column<T, V> setSortable(boolean sortable) { + getState().sortable = sortable; + return this; + } + + /** + * Gets whether the user can sort this column or not. + * + * @return {@code true} if the column can be sorted by the user; + * {@code false} if not + */ + public boolean isSortable() { + return getState(false).sortable; + } + + /** + * Sets the header caption for this column. + * + * @param caption + * the header caption + * + * @return this column + */ + public Column<T, V> setCaption(String caption) { + Objects.requireNonNull(caption, "Header caption can't be null"); + getState().caption = caption; + return this; + } + + /** + * Gets the header caption for this column. + * + * @return header caption + */ + public String getCaption() { + return getState(false).caption; + } + + /** + * Sets a comparator to use with in-memory sorting with this column. + * Sorting with a back-end is done using + * {@link Column#setSortProperty(String...)}. + * + * @param comparator + * the comparator to use when sorting data in this column + * @return this column + */ + public Column<T, V> setComparator(Comparator<T> comparator) { + Objects.requireNonNull(comparator, "Comparator can't be null"); + this.comparator = comparator; + return this; + } + + /** + * Gets the comparator to use with in-memory sorting for this column + * when sorting in the given direction. + * + * @param sortDirection + * the direction this column is sorted by + * @return comparator for this column + */ + public Comparator<T> getComparator(SortDirection sortDirection) { + Objects.requireNonNull(comparator, + "No comparator defined for sorted column."); + boolean reverse = sortDirection != SortDirection.ASCENDING; + return reverse ? comparator.reversed() : comparator; + } + + /** + * Sets strings describing back end properties to be used when sorting + * this column. This method is a short hand for + * {@link #setSortBuilder(Function)} that takes an array of strings and + * uses the same sorting direction for all of them. + * + * @param properties + * the array of strings describing backend properties + * @return this column + */ + public Column<T, V> setSortProperty(String... properties) { + Objects.requireNonNull(properties, "Sort properties can't be null"); + sortOrderProvider = dir -> Arrays.stream(properties) + .map(s -> new SortOrder<>(s, dir)); + return this; + } + + /** + * Sets the sort orders when sorting this column. The sort order + * provider is a function which provides {@link SortOrder} objects to + * describe how to sort by this column. + * + * @param provider + * the function to use when generating sort orders with the + * given direction + * @return this column + */ + public Column<T, V> setSortOrderProvider( + Function<SortDirection, Stream<SortOrder<String>>> provider) { + Objects.requireNonNull(provider, + "Sort order provider can't be null"); + sortOrderProvider = provider; + return this; + } + + /** + * Gets the sort orders to use with back-end sorting for this column + * when sorting in the given direction. + * + * @param direction + * the sorting direction + * @return stream of sort orders + */ + public Stream<SortOrder<String>> getSortOrder(SortDirection direction) { + return sortOrderProvider.apply(direction); + } + } + + private KeyMapper<Column<T, ?>> columnKeys = new KeyMapper<>(); + private Set<Column<T, ?>> columnSet = new HashSet<>(); + private List<SortOrder<Column<T, ?>>> sortOrder = new ArrayList<>(); + + /** + * Constructor for the {@link Grid} component. + */ + public Grid() { + setDataSource(DataSource.create()); + registerRpc(new GridServerRpcImpl()); + } + + /** + * Adds a new column to this {@link Grid} with given header caption and + * value provider. + * + * @param caption + * the header caption + * @param valueType + * the column value class + * @param valueProvider + * the value provider + * @param <V> + * the column value type + * + * @return the new column + */ + public <V> Column<T, V> addColumn(String caption, Class<V> valueType, + Function<T, V> valueProvider) { + Column<T, V> c = new Column<T, V>(caption, valueType, valueProvider); + + c.extend(this); + c.setId(columnKeys.key(c)); + columnSet.add(c); + addDataGenerator(c); + + return c; + } + + /** + * Removes the given column from this {@link Grid}. + * + * @param column + * the column to remove + */ + public void removeColumn(Column<T, ?> column) { + if (columnSet.remove(column)) { + columnKeys.remove(column); + removeDataGenerator(column); + column.remove(); + } + } + + /** + * Gets an unmodifiable collection of all columns currently in this + * {@link Grid}. + * + * @return unmodifiable collection of columns + */ + public Collection<Column<T, ?>> getColumns() { + return Collections.unmodifiableSet(columnSet); + } +} diff --git a/shared/src/main/java/com/vaadin/shared/ui/grid/ColumnState.java b/shared/src/main/java/com/vaadin/shared/ui/grid/ColumnState.java new file mode 100644 index 0000000000..bc04c4acb2 --- /dev/null +++ b/shared/src/main/java/com/vaadin/shared/ui/grid/ColumnState.java @@ -0,0 +1,25 @@ +/* + * 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.grid; + +import com.vaadin.shared.communication.SharedState; + +public class ColumnState extends SharedState { + + public String caption; + public String id; + public boolean sortable; +} diff --git a/uitest/src/main/java/com/vaadin/tests/components/grid/basics/DataObject.java b/uitest/src/main/java/com/vaadin/tests/components/grid/basics/DataObject.java new file mode 100644 index 0000000000..ebcdc34d7f --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/grid/basics/DataObject.java @@ -0,0 +1,93 @@ +package com.vaadin.tests.components.grid.basics; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Random; + +class DataObject { + + private static final int ROWS = 1000; + + private Integer rowNumber; + private String coordinates; + private String htmlString; + private Integer smallRandom; + private Integer bigRandom; + private Date date; + + public Integer getRowNumber() { + return rowNumber; + } + + public void setRowNumber(Integer rowNumber) { + this.rowNumber = rowNumber; + } + + public String getCoordinates() { + return coordinates; + } + + public void setCoordinates(String coordinates) { + this.coordinates = coordinates; + } + + public String getHtmlString() { + return htmlString; + } + + public void setHtmlString(String htmlString) { + this.htmlString = htmlString; + } + + public Integer getSmallRandom() { + return smallRandom; + } + + public void setSmallRandom(Integer smallRandom) { + this.smallRandom = smallRandom; + } + + public Integer getBigRandom() { + return bigRandom; + } + + public void setBigRandom(Integer bigRandom) { + this.bigRandom = bigRandom; + } + + public Date getDate() { + return date; + } + + public void setDate(Date date) { + this.date = date; + } + + static List<DataObject> generateObjects() { + List<DataObject> data = new ArrayList<>(); + + { + Random rand = new Random(); + rand.setSeed(13334); + long timestamp = 0; + for (int row = 0; row < ROWS; row++) { + DataObject obj = new DataObject(); + obj.setRowNumber(row); + obj.setCoordinates("(" + row + ", " + 0 + ")"); + obj.setHtmlString("<b>" + row + "</b>"); + // Random numbers + obj.setBigRandom(rand.nextInt()); + obj.setSmallRandom(rand.nextInt(5)); + + obj.setDate(new Date(timestamp)); + timestamp += 91250000; // a bit over a day, just to get + // variation + + data.add(obj); + } + } + return data; + } + +} diff --git a/uitest/src/main/java/com/vaadin/tests/components/grid/basics/GridBasics.java b/uitest/src/main/java/com/vaadin/tests/components/grid/basics/GridBasics.java new file mode 100644 index 0000000000..8ef56e7f64 --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/grid/basics/GridBasics.java @@ -0,0 +1,32 @@ +package com.vaadin.tests.components.grid.basics; + +import java.util.Date; +import java.util.List; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUIWithLog; +import com.vaadin.ui.Grid; + +public class GridBasics extends AbstractTestUIWithLog { + + private Grid<DataObject> grid; + + @Override + protected void setup(VaadinRequest request) { + List<DataObject> data = DataObject.generateObjects(); + + // Create grid + grid = new Grid<>(); + grid.setItems(data); + + grid.addColumn("Row Number", Integer.class, DataObject::getRowNumber); + grid.addColumn("Date", Date.class, DataObject::getDate); + grid.addColumn("HTML String", String.class, DataObject::getHtmlString); + grid.addColumn("Big Random", Integer.class, DataObject::getBigRandom); + grid.addColumn("Small Random", Integer.class, + DataObject::getSmallRandom); + + addComponent(grid); + } + +} diff --git a/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridBasicsTest.java b/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridBasicsTest.java new file mode 100644 index 0000000000..63f2c60f21 --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridBasicsTest.java @@ -0,0 +1,45 @@ +package com.vaadin.tests.components.grid.basics; + +import java.util.List; +import java.util.stream.Stream; + +import org.junit.Before; +import org.openqa.selenium.remote.DesiredCapabilities; + +import com.vaadin.testbench.elements.GridElement; +import com.vaadin.testbench.parallel.Browser; +import com.vaadin.tests.tb3.MultiBrowserTest; + +/** + * Base class for all {@link GridBasics} UI tests + */ +public abstract class GridBasicsTest extends MultiBrowserTest { + + /* Identical List of test data */ + private List<DataObject> testData; + + @Override + public List<DesiredCapabilities> getBrowsersToTest() { + // Most tests are run with only one browser. + return getBrowserCapabilities(Browser.PHANTOMJS); + } + + @Override + protected Class<?> getUIClass() { + return GridBasics.class; + } + + @Before + public void setUp() { + openTestURL(); + testData = DataObject.generateObjects(); + } + + protected GridElement getGrid() { + return $(GridElement.class).first(); + } + + protected Stream<DataObject> getTestData() { + return testData.stream(); + } +} diff --git a/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridContentTest.java b/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridContentTest.java new file mode 100644 index 0000000000..315282439a --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridContentTest.java @@ -0,0 +1,17 @@ +package com.vaadin.tests.components.grid.basics; + +import org.junit.Assert; +import org.junit.Test; + +public class GridContentTest extends GridBasicsTest { + + @Test(expected = AssertionError.class) + public void testHtmlRenderer() { + DataObject first = getTestData().findFirst().orElse(null); + Assert.assertEquals("Text content should match row number", + first.getRowNumber().toString(), + getGrid().getCell(0, 2).getText()); + Assert.assertEquals("HTML content did not match", first.getHtmlString(), + getGrid().getCell(0, 2).getAttribute("innerHTML")); + } +} diff --git a/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridSortingTest.java b/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridSortingTest.java new file mode 100644 index 0000000000..e9cd744719 --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/grid/basics/GridSortingTest.java @@ -0,0 +1,52 @@ +package com.vaadin.tests.components.grid.basics; + +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; + +import org.junit.Assert; +import org.junit.Test; +import org.openqa.selenium.Keys; +import org.openqa.selenium.remote.DesiredCapabilities; + +public class GridSortingTest extends GridBasicsTest { + + public static final Comparator<DataObject> BIG_RANDOM = Comparator + .comparing(DataObject::getBigRandom); + public static final Comparator<DataObject> SMALL_RANDOM = Comparator + .comparing(DataObject::getSmallRandom); + + @Override + public List<DesiredCapabilities> getBrowsersToTest() { + return getBrowsersSupportingShiftClick(); + } + + @Test + public void testSortBySingleColumnByUser() { + getGrid().getHeaderCell(0, 3).click(); + int i = 0; + for (Integer rowNumber : getTestData().sorted(BIG_RANDOM) + .map(DataObject::getRowNumber).limit(5) + .collect(Collectors.toList())) { + Assert.assertEquals( + "Grid was not sorted as expected, row number mismatch", + rowNumber.toString(), getGrid().getCell(i++, 0).getText()); + } + } + + @Test + public void testSortByMultipleColumnsByUser() { + getGrid().getHeaderCell(0, 4).click(); + getGrid().getHeaderCell(0, 3).click(15, 15, Keys.SHIFT); + + int i = 0; + for (Integer rowNumber : getTestData() + .sorted(SMALL_RANDOM.thenComparing(BIG_RANDOM)) + .map(DataObject::getRowNumber).limit(5) + .collect(Collectors.toList())) { + Assert.assertEquals( + "Grid was not sorted as expected, row number mismatch", + rowNumber.toString(), getGrid().getCell(i++, 0).getText()); + } + } +} |