Change-Id: I00cbe80ac72787de0c4cc88e1c223badc2c4ae89tags/8.0.0.alpha1
/* | |||||
* 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(); | |||||
} | |||||
} |
/* | |||||
* 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()); | |||||
} | |||||
} |
import com.vaadin.server.data.TypedDataGenerator; | 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. | * {@link DataCommunicator} and {@link TypedDataGenerator}s. | ||||
* | * | ||||
* @param <T> | * @param <T> | ||||
implements Listing<T> { | implements Listing<T> { | ||||
/* DataCommunicator for this Listing component */ | /* 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 | @Override | ||||
public void setDataSource(DataSource<T> dataSource) { | public void setDataSource(DataSource<T> dataSource) { |
/* | |||||
* 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); | |||||
} | |||||
} |
/* | |||||
* 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; | |||||
} |
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; | |||||
} | |||||
} |
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); | |||||
} | |||||
} |
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(); | |||||
} | |||||
} |
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")); | |||||
} | |||||
} |
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()); | |||||
} | |||||
} | |||||
} |