Change-Id: I00cbe80ac72787de0c4cc88e1c223badc2c4ae89tags/8.0.0.alpha1
@@ -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(); | |||
} | |||
} |
@@ -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()); | |||
} | |||
} |
@@ -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) { |
@@ -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); | |||
} | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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")); | |||
} | |||
} |
@@ -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()); | |||
} | |||
} | |||
} |