diff options
36 files changed, 2034 insertions, 165 deletions
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 index 7d906fc4af..828c795777 100644 --- a/client/src/main/java/com/vaadin/client/connectors/grid/GridConnector.java +++ b/client/src/main/java/com/vaadin/client/connectors/grid/GridConnector.java @@ -53,9 +53,7 @@ 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.client.widgets.Grid.FooterCell; import com.vaadin.client.widgets.Grid.FooterRow; -import com.vaadin.client.widgets.Grid.HeaderCell; import com.vaadin.client.widgets.Grid.HeaderRow; import com.vaadin.shared.MouseEventDetails; import com.vaadin.shared.data.sort.SortDirection; @@ -212,6 +210,11 @@ public class GridConnector extends AbstractListingConnector .map(this::getColumn).toArray(size -> new Column[size])); } + @OnStateChange("columnResizeMode") + void updateColumnResizeMode() { + getWidget().setColumnResizeMode(getState().columnResizeMode); + } + /** * Updates the grid header section on state change. */ @@ -235,18 +238,21 @@ public class GridConnector extends AbstractListingConnector } } - private void updateStaticRow(RowState rowState, Grid.StaticSection.StaticRow row) { + private void updateStaticRow(RowState rowState, + Grid.StaticSection.StaticRow row) { rowState.cells.forEach((columnId, cellState) -> { updateStaticCellFromState(row.getCell(getColumn(columnId)), cellState); }); - for (Map.Entry<CellState, Set<String>> cellGroupEntry : rowState.cellGroups.entrySet()) { + for (Map.Entry<CellState, Set<String>> cellGroupEntry : rowState.cellGroups + .entrySet()) { Set<String> group = cellGroupEntry.getValue(); - Grid.Column<?, ?>[] columns = - group.stream().map(idToColumn::get).toArray(size->new Grid.Column<?, ?>[size]); + Grid.Column<?, ?>[] columns = group.stream().map(idToColumn::get) + .toArray(size -> new Grid.Column<?, ?>[size]); // Set state to be the same as first in group. - updateStaticCellFromState(row.join(columns), cellGroupEntry.getKey()); + updateStaticCellFromState(row.join(columns), + cellGroupEntry.getKey()); } } diff --git a/client/src/main/java/com/vaadin/client/ui/VTextField.java b/client/src/main/java/com/vaadin/client/ui/VTextField.java index 1eb499505a..1ed84ab274 100644 --- a/client/src/main/java/com/vaadin/client/ui/VTextField.java +++ b/client/src/main/java/com/vaadin/client/ui/VTextField.java @@ -72,4 +72,5 @@ public class VTextField extends TextBoxBase public void onFocus(FocusEvent event) { addStyleDependentName(CLASSNAME_FOCUS); } + } diff --git a/client/src/main/java/com/vaadin/client/ui/dd/DragHandle.java b/client/src/main/java/com/vaadin/client/ui/dd/DragHandle.java index b800c5e53b..c5a9475d39 100644 --- a/client/src/main/java/com/vaadin/client/ui/dd/DragHandle.java +++ b/client/src/main/java/com/vaadin/client/ui/dd/DragHandle.java @@ -37,14 +37,14 @@ import com.vaadin.client.ui.dd.DragAndDropHandler.DragAndDropCallback; public class DragHandle { /** - * Callback interface for the DragHandle event life cycle + * Callback interface for the DragHandle event life cycle. */ public interface DragHandleCallback { /** - * Called when dragging starts + * Called when dragging starts. */ - public void onStart(); + void onStart(); /** * Called when the drag handle has moved. @@ -54,18 +54,18 @@ public class DragHandle { * @param deltaY * change in Y direction since start */ - public void onUpdate(double deltaX, double deltaY); + void onUpdate(double deltaX, double deltaY); /** * Called when the drag operation has been cancelled (usually by - * pressing ESC) + * pressing ESC). */ - public void onCancel(); + void onCancel(); /** - * Called when the drag operation completes successfully + * Called when the drag operation completes successfully. */ - public void onComplete(); + void onComplete(); } @@ -87,6 +87,20 @@ public class DragHandle { * later) both to provide the "-dragged" style and to make sure * that the drag handle can be properly styled (it's otherwise * invisible) + */ + public DragHandle(String baseName) { + this(baseName, null); + } + + /** + * Creates a new DragHandle. + * + * @param baseName + * CSS style name to use for this DragHandle element. This + * parameter is supplied to the constructor (rather than added + * later) both to provide the "-dragged" style and to make sure + * that the drag handle can be properly styled (it's otherwise + * invisible) * @param callback * Callback object allows hooking up the drag handle to the rest * of the program logic @@ -106,22 +120,28 @@ public class DragHandle { @Override public void onDrop() { removeDraggingStyle(); - userCallback.onComplete(); + if (userCallback != null) { + userCallback.onComplete(); + } } @Override public void onDragUpdate(Event e) { - double dx = WidgetUtil.getTouchOrMouseClientX(e) - startX; - double dy = WidgetUtil.getTouchOrMouseClientY(e) - startY; - userCallback.onUpdate(dx, dy); + if (userCallback != null) { + double dx = WidgetUtil.getTouchOrMouseClientX(e) - startX; + double dy = WidgetUtil.getTouchOrMouseClientY(e) - startY; + userCallback.onUpdate(dx, dy); + } } @Override public boolean onDragStart(Event e) { addDraggingStyle(); - startX = WidgetUtil.getTouchOrMouseClientX(e); - startY = WidgetUtil.getTouchOrMouseClientY(e); - userCallback.onStart(); + if (userCallback != null) { + startX = WidgetUtil.getTouchOrMouseClientX(e); + startY = WidgetUtil.getTouchOrMouseClientY(e); + userCallback.onStart(); + } return true; } @@ -133,7 +153,9 @@ public class DragHandle { @Override public void onDragCancel() { removeDraggingStyle(); - userCallback.onCancel(); + if (userCallback != null) { + userCallback.onCancel(); + } } private void addDraggingStyle() { @@ -157,6 +179,18 @@ public class DragHandle { } /** + * Sets the user-facing drag handle callback method. This allows code using + * the DragHandle to react to the situations where a drag handle first + * touched, when it's moved and when it's released. + * + * @param dragHandleCallback + * the callback object to use (can be null) + */ + public void setCallback(DragHandleCallback dragHandleCallback) { + userCallback = dragHandleCallback; + } + + /** * Returns the current parent element for this drag handle. May be null. * * @return an Element or null diff --git a/client/src/main/java/com/vaadin/client/ui/dd/VHtml5DragEvent.java b/client/src/main/java/com/vaadin/client/ui/dd/VHtml5DragEvent.java index 581d9263ac..830f668a00 100644 --- a/client/src/main/java/com/vaadin/client/ui/dd/VHtml5DragEvent.java +++ b/client/src/main/java/com/vaadin/client/ui/dd/VHtml5DragEvent.java @@ -90,8 +90,11 @@ public class VHtml5DragEvent extends NativeEvent { // Chrome >= v21 and Opera >= v? if (this.dataTransfer.items) { var item = this.dataTransfer.items[fileIndex]; - if (item.webkitGetAsEntry) { - return item.webkitGetAsEntry().isFile; + if (typeof item.webkitGetAsEntry == "function") { + var entry = item.webkitGetAsEntry(); + if (typeof entry !== "undefined" && entry !== null) { + return entry.isFile; + } } } diff --git a/client/src/main/java/com/vaadin/client/widget/escalator/ScrollbarBundle.java b/client/src/main/java/com/vaadin/client/widget/escalator/ScrollbarBundle.java index 2b615ebdb7..ed8d3cd57b 100644 --- a/client/src/main/java/com/vaadin/client/widget/escalator/ScrollbarBundle.java +++ b/client/src/main/java/com/vaadin/client/widget/escalator/ScrollbarBundle.java @@ -436,6 +436,9 @@ public abstract class ScrollbarBundle implements DeferredWorker { boolean offsetSizeBecomesGreaterThanScrollSize = showsScrollHandle() && newOffsetSizeIsGreaterThanScrollSize; if (offsetSizeBecomesGreaterThanScrollSize && getScrollPos() != 0) { + if (offsetSizeTemporaryScrollHandler != null) { + offsetSizeTemporaryScrollHandler.removeHandler(); + } // must be a field because Java insists. offsetSizeTemporaryScrollHandler = addScrollHandler( new ScrollHandler() { @@ -661,6 +664,9 @@ public abstract class ScrollbarBundle implements DeferredWorker { */ boolean delayedSizeSet = !BrowserInfo.get().isFirefox(); if (delayedSizeSet) { + if (scrollSizeTemporaryScrollHandler != null) { + scrollSizeTemporaryScrollHandler.removeHandler(); + } scrollSizeTemporaryScrollHandler = addScrollHandler( new ScrollHandler() { @Override diff --git a/client/src/main/java/com/vaadin/client/widgets/Escalator.java b/client/src/main/java/com/vaadin/client/widgets/Escalator.java index e3e0dfa0e4..e3953b436a 100644 --- a/client/src/main/java/com/vaadin/client/widgets/Escalator.java +++ b/client/src/main/java/com/vaadin/client/widgets/Escalator.java @@ -307,6 +307,8 @@ public class Escalator extends Widget static class JsniUtil { public static class TouchHandlerBundle { + public static final String POINTER_EVENT_TYPE_TOUCH = "touch"; + /** * A <a href= * "http://www.gwtproject.org/doc/latest/DevGuideCodingBasicsOverlay.html" @@ -339,6 +341,11 @@ public class Escalator extends Widget /*-{ return this.targetTouches[0].pageY; }-*/; + + public native String getPointerType() + /*-{ + return this.pointerType; + }-*/; } private final Escalator escalator; @@ -456,6 +463,15 @@ public class Escalator extends Widget } int pagePosition(CustomTouchEvent event) { + // Use native event's screen x and y for IE11 and Edge + // since there is no touches for these browsers (#18737) + if (isCurrentBrowserIE11OrEdge()) { + return vertical + ? event.getNativeEvent().getClientY() + + Window.getScrollTop() + : event.getNativeEvent().getClientX() + + Window.getScrollLeft(); + } JsArray<Touch> a = event.getNativeEvent().getTouches(); return vertical ? a.get(0).getPageY() : a.get(0).getPageX(); } @@ -495,7 +511,7 @@ public class Escalator extends Widget }; public void touchStart(final CustomTouchEvent event) { - if (event.getNativeEvent().getTouches().length() == 1) { + if (allowTouch(event)) { if (yMov == null) { yMov = new Movement(true); xMov = new Movement(false); @@ -543,6 +559,18 @@ public class Escalator extends Widget } } + // Allow touchStart for IE11 and Edge even though there is no touch + // (#18737), + // otherwise allow touch only if there is a single touch in the + // event + private boolean allowTouch(final TouchHandlerBundle.CustomTouchEvent event) { + if (isCurrentBrowserIE11OrEdge()) { + return (POINTER_EVENT_TYPE_TOUCH.equals(event.getPointerType())); + } else { + return (event.getNativeEvent().getTouches().length() == 1); + } + } + private double easingInOutCos(double val, double max) { return 0.5 - 0.5 * Math.cos(Math.PI * Math.signum(val) * Math.min(Math.abs(val), max) / max); @@ -881,7 +909,7 @@ public class Escalator extends Widget public native void detachScrollListener(Element element) /* - * Attaching events with JSNI instead of the GWT event mechanism because + * Detaching events with JSNI instead of the GWT event mechanism because * GWT didn't provide enough details in events, or triggering the event * handlers with GWT bindings was unsuccessful. Maybe, with more time * and skill, it could be done with better success. JavaScript overlay @@ -958,6 +986,50 @@ public class Escalator extends Widget element.removeEventListener("touchcancel", this.@com.vaadin.client.widgets.JsniWorkaround::touchEndFunction); }-*/; + /** + * Using pointerdown, pointermove, pointerup, and pointercancel for IE11 and Edge instead of + * touch* listeners (#18737) + * + * @param element + */ + public native void attachPointerEventListeners(Element element) + /* + * Attaching events with JSNI instead of the GWT event mechanism because + * GWT didn't provide enough details in events, or triggering the event + * handlers with GWT bindings was unsuccessful. Maybe, with more time + * and skill, it could be done with better success. JavaScript overlay + * types might work. This might also get rid of the JsniWorkaround + * class. + */ + /*-{ + element.addEventListener("pointerdown", this.@com.vaadin.client.widgets.JsniWorkaround::touchStartFunction); + element.addEventListener("pointermove", this.@com.vaadin.client.widgets.JsniWorkaround::touchMoveFunction); + element.addEventListener("pointerup", this.@com.vaadin.client.widgets.JsniWorkaround::touchEndFunction); + element.addEventListener("pointercancel", this.@com.vaadin.client.widgets.JsniWorkaround::touchEndFunction); + }-*/; + + /** + * Using pointerdown, pointermove, pointerup, and pointercancel for IE11 and Edge instead of + * touch* listeners (#18737) + * + * @param element + */ + public native void detachPointerEventListeners(Element element) + /* + * Detaching events with JSNI instead of the GWT event mechanism because + * GWT didn't provide enough details in events, or triggering the event + * handlers with GWT bindings was unsuccessful. Maybe, with more time + * and skill, it could be done with better success. JavaScript overlay + * types might work. This might also get rid of the JsniWorkaround + * class. + */ + /*-{ + element.removeEventListener("pointerdown", this.@com.vaadin.client.widgets.JsniWorkaround::touchStartFunction); + element.removeEventListener("pointermove", this.@com.vaadin.client.widgets.JsniWorkaround::touchMoveFunction); + element.removeEventListener("pointerup", this.@com.vaadin.client.widgets.JsniWorkaround::touchEndFunction); + element.removeEventListener("pointercancel", this.@com.vaadin.client.widgets.JsniWorkaround::touchEndFunction); + }-*/; + public void scrollToColumn(final int columnIndex, final ScrollDestination destination, final int padding) { assert columnIndex >= columnConfiguration.frozenColumns : "Can't scroll to a frozen column"; @@ -5717,7 +5789,13 @@ public class Escalator extends Widget scroller.attachScrollListener(verticalScrollbar.getElement()); scroller.attachScrollListener(horizontalScrollbar.getElement()); scroller.attachMousewheelListener(getElement()); - scroller.attachTouchListeners(getElement()); + + if (isCurrentBrowserIE11OrEdge()) { + // Touch listeners doesn't work for IE11 and Edge (#18737) + scroller.attachPointerEventListeners(getElement()); + } else { + scroller.attachTouchListeners(getElement()); + } } @Override @@ -5726,7 +5804,13 @@ public class Escalator extends Widget scroller.detachScrollListener(verticalScrollbar.getElement()); scroller.detachScrollListener(horizontalScrollbar.getElement()); scroller.detachMousewheelListener(getElement()); - scroller.detachTouchListeners(getElement()); + + if (isCurrentBrowserIE11OrEdge()) { + // Touch listeners doesn't work for IE11 and Edge (#18737) + scroller.detachPointerEventListeners(getElement()); + } else { + scroller.detachTouchListeners(getElement()); + } /* * We can call paintRemoveRows here, because static ranges are simple to @@ -6772,4 +6856,12 @@ public class Escalator extends Widget double getMinCellWidth(int colIndex) { return columnConfiguration.getMinCellWidth(colIndex); } + + /** + * Internal method for checking whether the browser is IE11 or Edge + * @return true only if the current browser is IE11, or Edge + */ + private static boolean isCurrentBrowserIE11OrEdge() { + return BrowserInfo.get().isIE11() || BrowserInfo.get().isEdge(); + } } diff --git a/client/src/main/java/com/vaadin/client/widgets/Grid.java b/client/src/main/java/com/vaadin/client/widgets/Grid.java index 368ad8693b..1ffda08ef7 100644 --- a/client/src/main/java/com/vaadin/client/widgets/Grid.java +++ b/client/src/main/java/com/vaadin/client/widgets/Grid.java @@ -37,6 +37,7 @@ import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.core.shared.GWT; import com.google.gwt.dom.client.BrowserEvents; import com.google.gwt.dom.client.DivElement; +import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.EventTarget; import com.google.gwt.dom.client.NativeEvent; @@ -174,6 +175,7 @@ import com.vaadin.client.widgets.Grid.StaticSection.StaticRow; import com.vaadin.shared.Range; import com.vaadin.shared.Registration; import com.vaadin.shared.data.sort.SortDirection; +import com.vaadin.shared.ui.grid.ColumnResizeMode; import com.vaadin.shared.ui.grid.GridConstants; import com.vaadin.shared.ui.grid.GridConstants.Section; import com.vaadin.shared.ui.grid.GridStaticCellType; @@ -4140,6 +4142,8 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, private AutoScroller autoScroller = new AutoScroller(this); + private ColumnResizeMode columnResizeMode = ColumnResizeMode.ANIMATED; + private DragAndDropHandler.DragAndDropCallback headerCellDndCallback = new DragAndDropCallback() { private final AutoScrollerCallback autoScrollerCallback = new AutoScrollerCallback() { @@ -5684,63 +5688,164 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, && staticRow instanceof HeaderRow && ((HeaderRow) staticRow).isDefault()) { + final DivElement resizeElement = Document.get() + .createDivElement(); + resizeElement.addClassName(getStylePrimaryName() + + "-column-resize-simple-indicator"); + final int column = cell.getColumn(); - DragHandle dragger = new DragHandle( - getStylePrimaryName() + "-column-resize-handle", - new DragHandleCallback() { - - private Column<?, T> col = getVisibleColumn( - column); - private double initialWidth = 0; - private double minCellWidth; - - @Override - public void onUpdate(double deltaX, - double deltaY) { - col.setWidth(Math.max(minCellWidth, - initialWidth + deltaX)); - } + final DragHandle dragger = new DragHandle( + getStylePrimaryName() + "-column-resize-handle"); + dragger.addTo(td); - @Override - public void onStart() { - initialWidth = col.getWidthActual(); - - minCellWidth = escalator.getMinCellWidth( - getVisibleColumns().indexOf(col)); - for (Column<?, T> c : getVisibleColumns()) { - if (selectionColumn == c) { - // Don't modify selection column. - continue; - } - - if (c.getWidth() < 0) { - c.setWidth(c.getWidthActual()); - fireEvent( - new ColumnResizeEvent<>(c)); - } - } + // Common functionality for drag handle callback + // implementations + abstract class AbstractDHCallback + implements DragHandleCallback { + protected Column<?, T> col = getVisibleColumn(column); + protected double initialWidth = 0; + protected double minCellWidth; + protected double width; + + protected void dragStarted() { + initialWidth = col.getWidthActual(); + width = initialWidth; + + minCellWidth = escalator.getMinCellWidth( + getVisibleColumns().indexOf(col)); + for (Column<?, T> c : getVisibleColumns()) { + if (selectionColumn == c) { + // Don't modify selection column. + continue; + } - WidgetUtil.setTextSelectionEnabled( - getElement(), false); + if (c.getWidth() < 0) { + c.setWidth(c.getWidthActual()); + fireEvent(new ColumnResizeEvent<>(c)); } + } - @Override - public void onComplete() { - fireEvent(new ColumnResizeEvent<>(col)); + WidgetUtil.setTextSelectionEnabled(getElement(), + false); + } - WidgetUtil.setTextSelectionEnabled( - getElement(), true); - } + protected void dragEnded() { + WidgetUtil.setTextSelectionEnabled(getElement(), + true); + } + } - @Override - public void onCancel() { - col.setWidth(initialWidth); + final DragHandleCallback simpleResizeMode = new AbstractDHCallback() { + @Override + protected void dragEnded() { + super.dragEnded(); + dragger.getElement().removeChild(resizeElement); + } - WidgetUtil.setTextSelectionEnabled( - getElement(), true); - } - }); - dragger.addTo(td); + @Override + public void onStart() { + dragStarted(); + dragger.getElement().appendChild(resizeElement); + resizeElement.getStyle().setLeft( + (dragger.getElement().getOffsetWidth() + - resizeElement.getOffsetWidth()) + * .5, + Unit.PX); + resizeElement.getStyle().setHeight( + col.grid.getOffsetHeight(), Unit.PX); + } + + @Override + public void onUpdate(double deltaX, double deltaY) { + width = Math.max(minCellWidth, + initialWidth + deltaX); + resizeElement.getStyle().setLeft( + (dragger.getElement().getOffsetWidth() + - resizeElement.getOffsetWidth()) + * .5 + (width - initialWidth), + Unit.PX); + } + + @Override + public void onCancel() { + dragEnded(); + } + + @Override + public void onComplete() { + dragEnded(); + + col.setWidth(width); + fireEvent(new ColumnResizeEvent<>(col)); + } + }; + + final DragHandleCallback animatedResizeMode = new AbstractDHCallback() { + @Override + public void onStart() { + dragStarted(); + } + + @Override + public void onUpdate(double deltaX, double deltaY) { + width = Math.max(minCellWidth, + initialWidth + deltaX); + col.setWidth(width); + } + + @Override + public void onCancel() { + dragEnded(); + col.setWidth(initialWidth); + } + + @Override + public void onComplete() { + dragEnded(); + col.setWidth(width); + fireEvent(new ColumnResizeEvent<>(col)); + } + }; + + // DragHandle gets assigned a 'master callback' that + // delegates + // functionality to the correct case-specific implementation + dragger.setCallback(new DragHandleCallback() { + + private DragHandleCallback currentCallback; + + @Override + public void onStart() { + switch (getColumnResizeMode()) { + case SIMPLE: + currentCallback = simpleResizeMode; + break; + case ANIMATED: + currentCallback = animatedResizeMode; + break; + default: + throw new UnsupportedOperationException( + "Support for current column resize mode is not yet implemented"); + } + + currentCallback.onStart(); + } + + @Override + public void onUpdate(double deltaX, double deltaY) { + currentCallback.onUpdate(deltaX, deltaY); + } + + @Override + public void onCancel() { + currentCallback.onCancel(); + } + + @Override + public void onComplete() { + currentCallback.onComplete(); + } + }); } cellFocusHandler.updateFocusedCellStyle(cell, container); @@ -5996,6 +6101,27 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, fireEvent(new GridEnabledEvent(enabled)); } + /** + * Sets the column resize mode to use. The default mode is + * {@link ColumnResizeMode.ANIMATED}. + * + * @param mode + * a ColumnResizeMode value + */ + public void setColumnResizeMode(ColumnResizeMode mode) { + columnResizeMode = mode; + } + + /** + * Returns the current column resize mode. The default mode is + * {@link ColumnResizeMode.ANIMATED}. + * + * @return a ColumnResizeMode value + */ + public ColumnResizeMode getColumnResizeMode() { + return columnResizeMode; + } + @Override public void setStylePrimaryName(String style) { super.setStylePrimaryName(style); diff --git a/compatibility-client/src/main/java/com/vaadin/v7/client/connectors/GridConnector.java b/compatibility-client/src/main/java/com/vaadin/v7/client/connectors/GridConnector.java index cf48586d01..cba6fc6565 100644 --- a/compatibility-client/src/main/java/com/vaadin/v7/client/connectors/GridConnector.java +++ b/compatibility-client/src/main/java/com/vaadin/v7/client/connectors/GridConnector.java @@ -389,7 +389,7 @@ public class GridConnector extends AbstractHasComponentsConnector } else { Collection<Column<?, JsonObject>> errorColumns; if (errorColumnsIds != null) { - errorColumns = new ArrayList<Grid.Column<?, JsonObject>>(); + errorColumns = new ArrayList<>(); for (String colId : errorColumnsIds) { errorColumns.add(columnIdToColumn.get(colId)); } @@ -435,7 +435,7 @@ public class GridConnector extends AbstractHasComponentsConnector public void onColumnReorder(ColumnReorderEvent<JsonObject> event) { if (!columnsUpdatedFromState) { List<Column<?, JsonObject>> columns = getWidget().getColumns(); - final List<String> newColumnOrder = new ArrayList<String>(); + final List<String> newColumnOrder = new ArrayList<>(); for (Column<?, JsonObject> column : columns) { if (column instanceof CustomGridColumn) { newColumnOrder.add(((CustomGridColumn) column).id); @@ -494,8 +494,8 @@ public class GridConnector extends AbstractHasComponentsConnector private class CustomDetailsGenerator implements HeightAwareDetailsGenerator { - private final Map<String, ComponentConnector> idToDetailsMap = new HashMap<String, ComponentConnector>(); - private final Map<String, Integer> idToRowIndex = new HashMap<String, Integer>(); + private final Map<String, ComponentConnector> idToDetailsMap = new HashMap<>(); + private final Map<String, Integer> idToRowIndex = new HashMap<>(); @Override public Widget getDetails(int rowIndex) { @@ -544,7 +544,7 @@ public class GridConnector extends AbstractHasComponentsConnector } public void updateConnectorHierarchy(List<ServerConnector> children) { - Set<String> connectorIds = new HashSet<String>(); + Set<String> connectorIds = new HashSet<>(); for (ServerConnector child : children) { if (child instanceof ComponentConnector) { connectorIds.add(child.getConnectorId()); @@ -553,7 +553,7 @@ public class GridConnector extends AbstractHasComponentsConnector } } - Set<String> removedDetails = new HashSet<String>(); + Set<String> removedDetails = new HashSet<>(); for (Entry<String, ComponentConnector> entry : idToDetailsMap .entrySet()) { ComponentConnector connector = entry.getValue(); @@ -626,9 +626,9 @@ public class GridConnector extends AbstractHasComponentsConnector /** * Maps a generated column id to a grid column instance */ - private Map<String, CustomGridColumn> columnIdToColumn = new HashMap<String, CustomGridColumn>(); + private Map<String, CustomGridColumn> columnIdToColumn = new HashMap<>(); - private List<String> columnOrder = new ArrayList<String>(); + private List<String> columnOrder = new ArrayList<>(); /** * {@link #columnsUpdatedFromState} is set to true when @@ -642,7 +642,7 @@ public class GridConnector extends AbstractHasComponentsConnector private RpcDataSource dataSource; /* Used to track Grid editor columns with validation errors */ - private final Map<Column<?, JsonObject>, String> columnToErrorMessage = new HashMap<Column<?, JsonObject>, String>(); + private final Map<Column<?, JsonObject>, String> columnToErrorMessage = new HashMap<>(); private ItemClickHandler itemClickHandler = new ItemClickHandler(); @@ -839,6 +839,11 @@ public class GridConnector extends AbstractHasComponentsConnector } } + // Column resize mode + if (stateChangeEvent.hasPropertyChanged("columnResizeMode")) { + getWidget().setColumnResizeMode(getState().columnResizeMode); + } + // Header and footer if (stateChangeEvent.hasPropertyChanged("header")) { updateHeaderFromState(getState().header); @@ -1101,7 +1106,7 @@ public class GridConnector extends AbstractHasComponentsConnector private void purgeRemovedColumns() { // Get columns still registered in the state - Set<String> columnsInState = new HashSet<String>(); + Set<String> columnsInState = new HashSet<>(); for (GridColumnState columnState : getState().columns) { columnsInState.add(columnState.id); } @@ -1126,7 +1131,7 @@ public class GridConnector extends AbstractHasComponentsConnector } private void onSortStateChange() { - List<SortOrder> sortOrder = new ArrayList<SortOrder>(); + List<SortOrder> sortOrder = new ArrayList<>(); String[] sortColumns = getState().sortColumns; SortDirection[] sortDirs = getState().sortDirs; @@ -1283,7 +1288,7 @@ public class GridConnector extends AbstractHasComponentsConnector * @return displayed error string */ private String getColumnErrors() { - List<String> errors = new ArrayList<String>(); + List<String> errors = new ArrayList<>(); for (Grid.Column<?, JsonObject> c : getWidget().getColumns()) { if (!(c instanceof CustomGridColumn)) { diff --git a/compatibility-client/src/main/java/com/vaadin/v7/client/ui/VTextField.java b/compatibility-client/src/main/java/com/vaadin/v7/client/ui/VTextField.java index ffaec8ad20..8357a7649a 100644 --- a/compatibility-client/src/main/java/com/vaadin/v7/client/ui/VTextField.java +++ b/compatibility-client/src/main/java/com/vaadin/v7/client/ui/VTextField.java @@ -34,6 +34,7 @@ import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.ui.TextBoxBase; import com.vaadin.client.ApplicationConnection; import com.vaadin.client.BrowserInfo; +import com.vaadin.client.DeferredWorker; import com.vaadin.client.WidgetUtil; import com.vaadin.client.ui.Field; import com.vaadin.shared.EventId; @@ -47,7 +48,7 @@ import com.vaadin.v7.shared.ui.textfield.TextFieldConstants; */ @Deprecated public class VTextField extends TextBoxBase implements Field, ChangeHandler, - FocusHandler, BlurHandler, KeyDownHandler { + FocusHandler, BlurHandler, KeyDownHandler, DeferredWorker { /** * The input node CSS classname. @@ -530,4 +531,9 @@ public class VTextField extends TextBoxBase implements Field, ChangeHandler, } possibleInputError = false; } + + @Override + public boolean isWorkPending() { + return scheduled; + } } diff --git a/compatibility-client/src/main/java/com/vaadin/v7/client/widgets/Grid.java b/compatibility-client/src/main/java/com/vaadin/v7/client/widgets/Grid.java index 49a6c6b986..0ea348553a 100644 --- a/compatibility-client/src/main/java/com/vaadin/v7/client/widgets/Grid.java +++ b/compatibility-client/src/main/java/com/vaadin/v7/client/widgets/Grid.java @@ -36,6 +36,7 @@ import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.core.shared.GWT; import com.google.gwt.dom.client.BrowserEvents; import com.google.gwt.dom.client.DivElement; +import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.EventTarget; import com.google.gwt.dom.client.NativeEvent; @@ -179,6 +180,7 @@ import com.vaadin.v7.client.widgets.Escalator.SubPartArguments; import com.vaadin.v7.client.widgets.Grid.Editor.State; import com.vaadin.v7.client.widgets.Grid.StaticSection.StaticCell; import com.vaadin.v7.client.widgets.Grid.StaticSection.StaticRow; +import com.vaadin.v7.shared.ui.grid.ColumnResizeMode; import com.vaadin.v7.shared.ui.grid.GridConstants; import com.vaadin.v7.shared.ui.grid.GridConstants.Section; import com.vaadin.v7.shared.ui.grid.GridStaticCellType; @@ -4105,6 +4107,8 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, private AutoScroller autoScroller = new AutoScroller(this); + private ColumnResizeMode columnResizeMode = ColumnResizeMode.ANIMATED; + private DragAndDropHandler.DragAndDropCallback headerCellDndCallback = new DragAndDropCallback() { private final AutoScrollerCallback autoScrollerCallback = new AutoScrollerCallback() { @@ -5691,63 +5695,164 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, && staticRow instanceof HeaderRow && ((HeaderRow) staticRow).isDefault()) { + final DivElement resizeElement = Document.get() + .createDivElement(); + resizeElement.addClassName(getStylePrimaryName() + + "-column-resize-simple-indicator"); + final int column = cell.getColumn(); - DragHandle dragger = new DragHandle( - getStylePrimaryName() + "-column-resize-handle", - new DragHandleCallback() { - - private Column<?, T> col = getVisibleColumn( - column); - private double initialWidth = 0; - private double minCellWidth; - - @Override - public void onUpdate(double deltaX, - double deltaY) { - col.setWidth(Math.max(minCellWidth, - initialWidth + deltaX)); - } + final DragHandle dragger = new DragHandle( + getStylePrimaryName() + "-column-resize-handle"); + dragger.addTo(td); - @Override - public void onStart() { - initialWidth = col.getWidthActual(); - - minCellWidth = escalator.getMinCellWidth( - getVisibleColumns().indexOf(col)); - for (Column<?, T> c : getVisibleColumns()) { - if (selectionColumn == c) { - // Don't modify selection column. - continue; - } - - if (c.getWidth() < 0) { - c.setWidth(c.getWidthActual()); - fireEvent( - new ColumnResizeEvent<>(c)); - } - } + // Common functionality for drag handle callback + // implementations + abstract class AbstractDHCallback + implements DragHandleCallback { + protected Column<?, T> col = getVisibleColumn(column); + protected double initialWidth = 0; + protected double minCellWidth; + protected double width; + + protected void dragStarted() { + initialWidth = col.getWidthActual(); + width = initialWidth; + + minCellWidth = escalator.getMinCellWidth( + getVisibleColumns().indexOf(col)); + for (Column<?, T> c : getVisibleColumns()) { + if (selectionColumn == c) { + // Don't modify selection column. + continue; + } - WidgetUtil.setTextSelectionEnabled( - getElement(), false); + if (c.getWidth() < 0) { + c.setWidth(c.getWidthActual()); + fireEvent(new ColumnResizeEvent<>(c)); } + } - @Override - public void onComplete() { - fireEvent(new ColumnResizeEvent<>(col)); + WidgetUtil.setTextSelectionEnabled(getElement(), + false); + } - WidgetUtil.setTextSelectionEnabled( - getElement(), true); - } + protected void dragEnded() { + WidgetUtil.setTextSelectionEnabled(getElement(), + true); + } + } - @Override - public void onCancel() { - col.setWidth(initialWidth); + final DragHandleCallback simpleResizeMode = new AbstractDHCallback() { + @Override + protected void dragEnded() { + super.dragEnded(); + dragger.getElement().removeChild(resizeElement); + } - WidgetUtil.setTextSelectionEnabled( - getElement(), true); - } - }); - dragger.addTo(td); + @Override + public void onStart() { + dragStarted(); + dragger.getElement().appendChild(resizeElement); + resizeElement.getStyle().setLeft( + (dragger.getElement().getOffsetWidth() + - resizeElement.getOffsetWidth()) + * .5, + Unit.PX); + resizeElement.getStyle().setHeight( + col.grid.getOffsetHeight(), Unit.PX); + } + + @Override + public void onUpdate(double deltaX, double deltaY) { + width = Math.max(minCellWidth, + initialWidth + deltaX); + resizeElement.getStyle().setLeft( + (dragger.getElement().getOffsetWidth() + - resizeElement.getOffsetWidth()) + * .5 + (width - initialWidth), + Unit.PX); + } + + @Override + public void onCancel() { + dragEnded(); + } + + @Override + public void onComplete() { + dragEnded(); + + col.setWidth(width); + fireEvent(new ColumnResizeEvent<>(col)); + } + }; + + final DragHandleCallback animatedResizeMode = new AbstractDHCallback() { + @Override + public void onStart() { + dragStarted(); + } + + @Override + public void onUpdate(double deltaX, double deltaY) { + width = Math.max(minCellWidth, + initialWidth + deltaX); + col.setWidth(width); + } + + @Override + public void onCancel() { + dragEnded(); + col.setWidth(initialWidth); + } + + @Override + public void onComplete() { + dragEnded(); + col.setWidth(width); + fireEvent(new ColumnResizeEvent<>(col)); + } + }; + + // DragHandle gets assigned a 'master callback' that + // delegates + // functionality to the correct case-specific implementation + dragger.setCallback(new DragHandleCallback() { + + private DragHandleCallback currentCallback; + + @Override + public void onStart() { + switch (getColumnResizeMode()) { + case SIMPLE: + currentCallback = simpleResizeMode; + break; + case ANIMATED: + currentCallback = animatedResizeMode; + break; + default: + throw new UnsupportedOperationException( + "Support for current column resize mode is not yet implemented"); + } + + currentCallback.onStart(); + } + + @Override + public void onUpdate(double deltaX, double deltaY) { + currentCallback.onUpdate(deltaX, deltaY); + } + + @Override + public void onCancel() { + currentCallback.onCancel(); + } + + @Override + public void onComplete() { + currentCallback.onComplete(); + } + }); } cellFocusHandler.updateFocusedCellStyle(cell, container); @@ -5979,6 +6084,27 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, return enabled; } + /** + * Sets the column resize mode to use. The default mode is + * {@link ColumnResizeMode.ANIMATED}. + * + * @param mode + * a ColumnResizeMode value + */ + public void setColumnResizeMode(ColumnResizeMode mode) { + columnResizeMode = mode; + } + + /** + * Returns the current column resize mode. The default mode is + * {@link ColumnResizeMode.ANIMATED}. + * + * @return a ColumnResizeMode value + */ + public ColumnResizeMode getColumnResizeMode() { + return columnResizeMode; + } + @Override public void setEnabled(boolean enabled) { if (enabled == this.enabled) { diff --git a/compatibility-server/src/main/java/com/vaadin/v7/data/fieldgroup/FieldGroup.java b/compatibility-server/src/main/java/com/vaadin/v7/data/fieldgroup/FieldGroup.java index 1055d1921d..553bbc753c 100644 --- a/compatibility-server/src/main/java/com/vaadin/v7/data/fieldgroup/FieldGroup.java +++ b/compatibility-server/src/main/java/com/vaadin/v7/data/fieldgroup/FieldGroup.java @@ -99,6 +99,13 @@ public class FieldGroup implements Serializable { public void setItemDataSource(Item itemDataSource) { this.itemDataSource = itemDataSource; + bindFields(); + } + + /** + * Binds all fields to the properties in the item in use. + */ + protected void bindFields() { for (Field<?> f : fieldToPropertyId.keySet()) { bind(f, fieldToPropertyId.get(f)); } @@ -254,20 +261,7 @@ public class FieldGroup implements Serializable { fieldToPropertyId.put(field, propertyId); propertyIdToField.put(propertyId, field); if (itemDataSource == null) { - // Clear any possible existing binding to clear the field - field.setPropertyDataSource(null); - boolean fieldReadOnly = field.isReadOnly(); - if (!fieldReadOnly) { - field.clear(); - } else { - // Temporarily make the field read-write so we can clear the - // value. Needed because setPropertyDataSource(null) does not - // currently clear the field - // (https://dev.vaadin.com/ticket/14733) - field.setReadOnly(false); - field.clear(); - field.setReadOnly(true); - } + clearField(field); // Will be bound when data source is set return; @@ -279,6 +273,29 @@ public class FieldGroup implements Serializable { } /** + * Clears field and any possible existing binding. + * + * @param field + * The field to be cleared + */ + protected void clearField(Field<?> field) { + // Clear any possible existing binding to clear the field + field.setPropertyDataSource(null); + boolean fieldReadOnly = field.isReadOnly(); + if (!fieldReadOnly) { + field.clear(); + } else { + // Temporarily make the field read-write so we can clear the + // value. Needed because setPropertyDataSource(null) does not + // currently clear the field + // (https://dev.vaadin.com/ticket/14733) + field.setReadOnly(false); + field.clear(); + field.setReadOnly(true); + } + } + + /** * Wrap property to transactional property. */ protected <T> Property.Transactional<T> wrapInTransactionalProperty( diff --git a/compatibility-server/src/main/java/com/vaadin/v7/ui/Grid.java b/compatibility-server/src/main/java/com/vaadin/v7/ui/Grid.java index f5399f60ce..e48e58824f 100644 --- a/compatibility-server/src/main/java/com/vaadin/v7/ui/Grid.java +++ b/compatibility-server/src/main/java/com/vaadin/v7/ui/Grid.java @@ -104,6 +104,7 @@ import com.vaadin.v7.event.SelectionEvent.SelectionListener; import com.vaadin.v7.event.SelectionEvent.SelectionNotifier; import com.vaadin.v7.server.communication.data.DataGenerator; import com.vaadin.v7.server.communication.data.RpcDataProviderExtension; +import com.vaadin.v7.shared.ui.grid.ColumnResizeMode; import com.vaadin.v7.shared.ui.grid.EditorClientRpc; import com.vaadin.v7.shared.ui.grid.EditorServerRpc; import com.vaadin.v7.shared.ui.grid.GridClientRpc; @@ -566,6 +567,35 @@ public class Grid extends AbstractComponent } return field; } + + @Override + protected void bindFields() { + List<Field<?>> fields = new ArrayList<>(getFields()); + Item itemDataSource = getItemDataSource(); + + if (itemDataSource == null) { + unbindFields(fields); + } else { + bindFields(fields, itemDataSource); + } + } + + private void unbindFields(List<Field<?>> fields) { + for (Field<?> field : fields) { + clearField(field); + unbind(field); + field.setParent(null); + } + } + + private void bindFields(List<Field<?>> fields, Item itemDataSource) { + for (Field<?> field : fields) { + if (itemDataSource + .getItemProperty(getPropertyId(field)) != null) { + bind(field, getPropertyId(field)); + } + } + } } /** @@ -2242,8 +2272,9 @@ public class Grid extends AbstractComponent Renderer<?> renderer = column.getRenderer(); Item item = cell.getItem(); - Object modelValue = item.getItemProperty(cell.getPropertyId()) - .getValue(); + Property itemProperty = item.getItemProperty(cell.getPropertyId()); + Object modelValue = itemProperty == null ? null + : itemProperty.getValue(); data.put(columnKeys.key(cell.getPropertyId()), AbstractRenderer .encodeValue(modelValue, renderer, converter, getLocale())); @@ -4583,6 +4614,12 @@ public class Grid extends AbstractComponent private boolean editorSaving = false; private FieldGroup editorFieldGroup = new CustomFieldGroup(); + /** + * Poperty ID to Field mapping that stores editor fields set by + * {@link #setEditorField(Object, Field)}. + */ + private Map<Object, Field<?>> editorFields = new HashMap<>(); + private CellStyleGenerator cellStyleGenerator; private RowStyleGenerator rowStyleGenerator; @@ -5286,6 +5323,27 @@ public class Grid extends AbstractComponent } /** + * Sets the column resize mode to use. The default mode is + * {@link ColumnResizeMode#ANIMATED}. + * + * @param mode + * a ColumnResizeMode value + */ + public void setColumnResizeMode(ColumnResizeMode mode) { + getState().columnResizeMode = mode; + } + + /** + * Returns the current column resize mode. The default mode is + * {@link ColumnResizeMode#ANIMATED}. + * + * @return a ColumnResizeMode value + */ + public ColumnResizeMode getColumnResizeMode() { + return getState(false).columnResizeMode; + } + + /** * Creates a new column based on a property id and appends it as the last * column. * @@ -6868,6 +6926,16 @@ public class Grid extends AbstractComponent Field<?> editor = editorFieldGroup.getField(propertyId); + // If field group has no field for this property, see if we have it + // stored + if (editor == null) { + editor = editorFields.get(propertyId); + if (editor != null) { + editorFieldGroup.bind(editor, propertyId); + } + } + + // Otherwise try to build one try { if (editor == null) { editor = editorFieldGroup.buildAndBind(propertyId); @@ -6923,8 +6991,9 @@ public class Grid extends AbstractComponent editorFieldGroup.setItemDataSource(item); for (Column column : getColumns()) { - column.getState().editorConnector = getEditorField( - column.getPropertyId()); + column.getState().editorConnector = item + .getItemProperty(column.getPropertyId()) == null ? null + : getEditorField(column.getPropertyId()); } editorActive = true; @@ -6953,6 +7022,9 @@ public class Grid extends AbstractComponent field.setParent(this); editorFieldGroup.bind(field, propertyId); } + + // Store field for this property for future reference + editorFields.put(propertyId, field); } /** diff --git a/compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/grid/GridEditorMissingPropertyTest.java b/compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/grid/GridEditorMissingPropertyTest.java new file mode 100644 index 0000000000..be6334f8cc --- /dev/null +++ b/compatibility-server/src/test/java/com/vaadin/v7/tests/server/component/grid/GridEditorMissingPropertyTest.java @@ -0,0 +1,334 @@ +/* + * 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.v7.tests.server.component.grid; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.v7.data.Item; +import com.vaadin.v7.data.Property; +import com.vaadin.v7.data.fieldgroup.FieldGroup; +import com.vaadin.v7.data.util.AbstractInMemoryContainer; +import com.vaadin.v7.data.util.BeanItem; +import com.vaadin.v7.ui.Field; +import com.vaadin.v7.ui.PasswordField; +import com.vaadin.v7.ui.TextField; + +public class GridEditorMissingPropertyTest { + + private static final String PROPERTY_NAME = "name"; + private static final String PROPERTY_SIZE = "size"; + + private static final String FOLDER_NAME_BEFORE = "Folder name"; + private static final String FOLDER_NAME_AFTER = "Modified folder name"; + private static final String FILE_NAME_BEFORE = "File name"; + private static final String FILE_NAME_AFTER = "Modified file name"; + private static final String FILE_SIZE_BEFORE = "10kB"; + private static final String FILE_SIZE_AFTER = "20MB"; + + private final Grid grid = new Grid(); + + // Test items + private final Folder folder = new Folder(FOLDER_NAME_BEFORE); + private final File file = new File(FILE_NAME_BEFORE, FILE_SIZE_BEFORE); + + @Before + public void setup() throws SecurityException, NoSuchMethodException { + final BeanItem<Entry> folderItem = new BeanItem<>(folder); + final BeanItem<Entry> childItem = new BeanItem<>(file); + + @SuppressWarnings("unchecked") + TestContainer container = new TestContainer( + Arrays.asList(folderItem, childItem), + Arrays.asList(PROPERTY_NAME, PROPERTY_SIZE)); + + grid.setContainerDataSource(container); + grid.setSelectionMode(Grid.SelectionMode.SINGLE); + grid.setEditorEnabled(true); + } + + @Test + public void testBindFields() { + FieldGroup fieldGroup = grid.getEditorFieldGroup(); + + // Item with incomplete property set + fieldGroup.setItemDataSource( + grid.getContainerDataSource().getItem(folder)); + grid.getColumn(PROPERTY_NAME).getEditorField(); // called in + // grid.doEditItem + assertTrue("Properties in item should be bound", + fieldGroup.getBoundPropertyIds().contains(PROPERTY_NAME)); + assertFalse("Properties not present in item should not be bound", + fieldGroup.getBoundPropertyIds().contains(PROPERTY_SIZE)); + assertTrue("All of item's properties should be bound", + fieldGroup.getUnboundPropertyIds().isEmpty()); + + // Unbind all fields + fieldGroup.setItemDataSource(null); + assertTrue("No properties should be bound", + fieldGroup.getBoundPropertyIds().isEmpty()); + assertTrue("No unbound properties should exist", + fieldGroup.getUnboundPropertyIds().isEmpty()); + + // Item with complete property set + fieldGroup + .setItemDataSource(grid.getContainerDataSource().getItem(file)); + grid.getColumn(PROPERTY_NAME).getEditorField(); + grid.getColumn(PROPERTY_SIZE).getEditorField(); + assertTrue("Properties in item should be bound", + fieldGroup.getBoundPropertyIds().contains(PROPERTY_NAME)); + assertTrue("Properties in item should be bound", + fieldGroup.getBoundPropertyIds().contains(PROPERTY_SIZE)); + assertTrue("All of item's properties should be bound", + fieldGroup.getUnboundPropertyIds().isEmpty()); + + // Unbind all fields + fieldGroup.setItemDataSource(null); + assertTrue("No properties should be bound", + fieldGroup.getBoundPropertyIds().isEmpty()); + assertTrue("No unbound properties should exist", + fieldGroup.getUnboundPropertyIds().isEmpty()); + } + + @Test + public void testSetEditorField() { + FieldGroup fieldGroup = grid.getEditorFieldGroup(); + Field editorField = new PasswordField(); + + // Explicitly set editor field + fieldGroup.setItemDataSource( + grid.getContainerDataSource().getItem(folder)); + grid.getColumn(PROPERTY_NAME).setEditorField(editorField); + assertTrue("Editor field should be the one that was previously set", + grid.getColumn(PROPERTY_NAME).getEditorField() == editorField); + + // Reset item + fieldGroup.setItemDataSource(null); + fieldGroup + .setItemDataSource(grid.getContainerDataSource().getItem(file)); + assertTrue("Editor field should be the one that was previously set", + grid.getColumn(PROPERTY_NAME).getEditorField() == editorField); + } + + @Test + public void testEditCell() { + // Row with missing property + startEdit(folder); + assertEquals(folder, grid.getEditedItemId()); + assertEquals(getEditedItem(), + grid.getEditorFieldGroup().getItemDataSource()); + + assertEquals(FOLDER_NAME_BEFORE, + grid.getColumn(PROPERTY_NAME).getEditorField().getValue()); + try { + grid.getColumn(PROPERTY_SIZE).getEditorField(); + fail("Grid.editorFieldGroup should throw BindException by default"); + } catch (FieldGroup.BindException e) { + // BindException is thrown using the default FieldGroup + } + grid.cancelEditor(); + + // Row with all properties + startEdit(file); + assertEquals(file, grid.getEditedItemId()); + assertEquals(getEditedItem(), + grid.getEditorFieldGroup().getItemDataSource()); + + assertEquals(FILE_NAME_BEFORE, + grid.getColumn(PROPERTY_NAME).getEditorField().getValue()); + assertEquals(FILE_SIZE_BEFORE, + grid.getColumn(PROPERTY_SIZE).getEditorField().getValue()); + grid.cancelEditor(); + } + + @Test + public void testCancelEditor() { + // Row with all properties + testCancel(file, PROPERTY_NAME, FILE_NAME_BEFORE, FILE_NAME_AFTER); + testCancel(file, PROPERTY_SIZE, FILE_SIZE_BEFORE, FILE_SIZE_AFTER); + + // Row with missing property + testCancel(folder, PROPERTY_NAME, FOLDER_NAME_BEFORE, + FOLDER_NAME_AFTER); + } + + private void testCancel(Object itemId, String propertyId, + String valueBefore, String valueAfter) { + startEdit(itemId); + + TextField field = (TextField) grid.getColumn(propertyId) + .getEditorField(); + field.setValue(valueAfter); + + Property<?> datasource = field.getPropertyDataSource(); + + grid.cancelEditor(); + assertFalse(grid.isEditorActive()); + assertNull(grid.getEditedItemId()); + assertFalse(field.isModified()); + assertEquals("", field.getValue()); + assertEquals(valueBefore, datasource.getValue()); + assertNull(field.getPropertyDataSource()); + assertNull(grid.getEditorFieldGroup().getItemDataSource()); + } + + @Test + public void testSaveEditor() throws Exception { + // Row with all properties + testSave(file, PROPERTY_SIZE, FILE_SIZE_BEFORE, FILE_SIZE_AFTER); + + // Row with missing property + testSave(folder, PROPERTY_NAME, FOLDER_NAME_BEFORE, FOLDER_NAME_AFTER); + } + + private void testSave(Object itemId, String propertyId, String valueBefore, + String valueAfter) throws Exception { + startEdit(itemId); + TextField field = (TextField) grid.getColumn(propertyId) + .getEditorField(); + + field.setValue(valueAfter); + assertEquals(valueBefore, field.getPropertyDataSource().getValue()); + + grid.saveEditor(); + assertTrue(grid.isEditorActive()); + assertFalse(field.isModified()); + assertEquals(valueAfter, field.getValue()); + assertEquals(valueAfter, getEditedProperty(propertyId).getValue()); + grid.cancelEditor(); + } + + private Item getEditedItem() { + assertNotNull(grid.getEditedItemId()); + return grid.getContainerDataSource().getItem(grid.getEditedItemId()); + } + + private Property<?> getEditedProperty(Object propertyId) { + return getEditedItem().getItemProperty(propertyId); + } + + private void startEdit(Object itemId) { + grid.setEditorEnabled(true); + grid.editItem(itemId); + // Simulate succesful client response to actually start the editing. + grid.doEditItem(); + } + + private class TestContainer + extends AbstractInMemoryContainer<Object, String, BeanItem> { + + private final List<BeanItem<Entry>> items; + private final List<String> pids; + + public TestContainer(List<BeanItem<Entry>> items, List<String> pids) { + this.items = items; + this.pids = pids; + } + + @Override + protected List<Object> getAllItemIds() { + List<Object> ids = new ArrayList<>(); + for (BeanItem<Entry> item : items) { + ids.add(item.getBean()); + } + return ids; + } + + @Override + protected BeanItem<Entry> getUnfilteredItem(Object itemId) { + for (BeanItem<Entry> item : items) { + if (item.getBean().equals(itemId)) { + return item; + } + } + return null; + } + + @Override + public Collection<?> getContainerPropertyIds() { + return pids; + } + + @Override + public Property getContainerProperty(Object itemId, Object propertyId) { + return getItem(itemId).getItemProperty(propertyId); + } + + @Override + public Class<?> getType(Object propertyId) { + return String.class; + } + } + + public class Entry { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Entry(String name) { + this.name = name; + } + } + + public class Folder extends Entry { + + public Folder(String name) { + super(name); + } + } + + public class File extends Entry { + private String size; + + public File(String name, String size) { + super(name); + this.size = size; + } + + public String getSize() { + return size; + } + + public void setSize(String size) { + this.size = size; + } + } + + private class Grid extends com.vaadin.v7.ui.Grid { + @Override + protected void doEditItem() { + super.doEditItem(); + } + } +} diff --git a/compatibility-shared/src/main/java/com/vaadin/v7/shared/ui/grid/ColumnResizeMode.java b/compatibility-shared/src/main/java/com/vaadin/v7/shared/ui/grid/ColumnResizeMode.java new file mode 100644 index 0000000000..6f3de6a185 --- /dev/null +++ b/compatibility-shared/src/main/java/com/vaadin/v7/shared/ui/grid/ColumnResizeMode.java @@ -0,0 +1,36 @@ +/* + * 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.v7.shared.ui.grid; + +/** + * Collection of modes used for resizing columns in the Grid. + */ +public enum ColumnResizeMode { + + /** + * When column resize mode is set to Animated, columns are resized as they + * are dragged. + */ + ANIMATED, + + /** + * When column resize mode is set to Simple, dragging to resize a column + * will show a marker, and the column will resize only after the mouse + * button or touch is released. + */ + SIMPLE + +}
\ No newline at end of file diff --git a/compatibility-shared/src/main/java/com/vaadin/v7/shared/ui/grid/GridState.java b/compatibility-shared/src/main/java/com/vaadin/v7/shared/ui/grid/GridState.java index 4befc1fe09..a4c701c64b 100644 --- a/compatibility-shared/src/main/java/com/vaadin/v7/shared/ui/grid/GridState.java +++ b/compatibility-shared/src/main/java/com/vaadin/v7/shared/ui/grid/GridState.java @@ -142,14 +142,19 @@ public class GridState extends TabIndexState { } /** + * Column resize mode in grid. + */ + public ColumnResizeMode columnResizeMode = ColumnResizeMode.ANIMATED; + + /** * Columns in grid. */ - public List<GridColumnState> columns = new ArrayList<GridColumnState>(); + public List<GridColumnState> columns = new ArrayList<>(); /** * Column order in grid. */ - public List<String> columnOrder = new ArrayList<String>(); + public List<String> columnOrder = new ArrayList<>(); public GridStaticSectionState header = new GridStaticSectionState(); diff --git a/compatibility-themes/src/main/themes/VAADIN/themes/base/escalator/escalator.scss b/compatibility-themes/src/main/themes/VAADIN/themes/base/escalator/escalator.scss index 1abf754584..10a21c65d9 100644 --- a/compatibility-themes/src/main/themes/VAADIN/themes/base/escalator/escalator.scss +++ b/compatibility-themes/src/main/themes/VAADIN/themes/base/escalator/escalator.scss @@ -75,6 +75,8 @@ } .#{$primaryStyleName}-body { + -ms-touch-action: none; + touch-action: none; z-index: 0; top: 0; diff --git a/compatibility-themes/src/main/themes/VAADIN/themes/base/grid/grid.scss b/compatibility-themes/src/main/themes/VAADIN/themes/base/grid/grid.scss index fdbf2a0cf5..2cbb1c8616 100644 --- a/compatibility-themes/src/main/themes/VAADIN/themes/base/grid/grid.scss +++ b/compatibility-themes/src/main/themes/VAADIN/themes/base/grid/grid.scss @@ -276,6 +276,22 @@ $v-grid-details-border-bottom-stripe: 1px solid darken($v-grid-row-background-co -ms-user-select: none; user-select: none; } + + .#{$primaryStyleName}-column-resize-simple-indicator { + position: absolute; + width: 3px; + top: 0px; + left: $v-grid-cell-padding-horizontal; + z-index: 9001; + background: #fff; + box-shadow: 0px 0px 5px #000; + + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + } // Footer diff --git a/server/src/main/java/com/vaadin/ui/Grid.java b/server/src/main/java/com/vaadin/ui/Grid.java index 120640bd53..7b868ee542 100644 --- a/server/src/main/java/com/vaadin/ui/Grid.java +++ b/server/src/main/java/com/vaadin/ui/Grid.java @@ -68,6 +68,7 @@ import com.vaadin.shared.Registration; import com.vaadin.shared.data.DataCommunicatorConstants; import com.vaadin.shared.data.sort.SortDirection; import com.vaadin.shared.ui.grid.AbstractGridExtensionState; +import com.vaadin.shared.ui.grid.ColumnResizeMode; import com.vaadin.shared.ui.grid.ColumnState; import com.vaadin.shared.ui.grid.DetailsManagerState; import com.vaadin.shared.ui.grid.GridConstants; @@ -79,6 +80,7 @@ import com.vaadin.shared.ui.grid.HeightMode; import com.vaadin.shared.ui.grid.SectionState; import com.vaadin.shared.util.SharedUtil; import com.vaadin.ui.Grid.FooterRow; +import com.vaadin.ui.Grid.SelectionMode; import com.vaadin.ui.components.grid.AbstractSelectionModel; import com.vaadin.ui.components.grid.EditorComponentGenerator; import com.vaadin.ui.components.grid.EditorImpl; @@ -845,7 +847,6 @@ public class Grid<T> extends AbstractListing<T> if (column != null && column.isResizable()) { column.getState().width = pixels; fireColumnResizeEvent(column, true); - markAsDirty(); } } } @@ -3466,6 +3467,27 @@ public class Grid<T> extends AbstractListing<T> } /** + * Sets the column resize mode to use. The default mode is + * {@link ColumnResizeMode#ANIMATED}. + * + * @param mode + * a ColumnResizeMode value + */ + public void setColumnResizeMode(ColumnResizeMode mode) { + getState().columnResizeMode = mode; + } + + /** + * Returns the current column resize mode. The default mode is + * {@link ColumnResizeMode#ANIMATED}. + * + * @return a ColumnResizeMode value + */ + public ColumnResizeMode getColumnResizeMode() { + return getState(false).columnResizeMode; + } + + /** * Creates a new Editor instance. Can be overridden to create a custom * Editor. If the Editor is a {@link AbstractGridExtension}, it will be * automatically added to {@link DataCommunicator}. @@ -3744,7 +3766,6 @@ public class Grid<T> extends AbstractListing<T> getDataCommunicator().setInMemorySorting(null); return; } - sortOrder.addAll(order); sort(userOriginated); } diff --git a/server/src/main/java/com/vaadin/ui/GridLayout.java b/server/src/main/java/com/vaadin/ui/GridLayout.java index f56a90be95..e4562f81ce 100644 --- a/server/src/main/java/com/vaadin/ui/GridLayout.java +++ b/server/src/main/java/com/vaadin/ui/GridLayout.java @@ -740,7 +740,7 @@ public class GridLayout extends AbstractLayout // Forget expands for removed columns if (columns < getColumns()) { - for (int i = columns - 1; i < getColumns(); i++) { + for (int i = columns; i < getColumns(); i++) { columnExpandRatio.remove(i); getState().explicitColRatios.remove(i); } @@ -790,7 +790,7 @@ public class GridLayout extends AbstractLayout } // Forget expands for removed rows if (rows < getRows()) { - for (int i = rows - 1; i < getRows(); i++) { + for (int i = rows; i < getRows(); i++) { rowExpandRatio.remove(i); getState().explicitRowRatios.remove(i); } diff --git a/server/src/test/java/com/vaadin/tests/server/component/gridlayout/GridLayoutTest.java b/server/src/test/java/com/vaadin/tests/server/component/gridlayout/GridLayoutTest.java index 1eabf2fb62..64b466612f 100644 --- a/server/src/test/java/com/vaadin/tests/server/component/gridlayout/GridLayoutTest.java +++ b/server/src/test/java/com/vaadin/tests/server/component/gridlayout/GridLayoutTest.java @@ -75,6 +75,30 @@ public class GridLayoutTest { assertOrder(grid, new int[] { 0, 1, 2, 3 }); } + @Test + public void removeRowsExpandRatiosPreserved() { + GridLayout gl = new GridLayout(3, 3); + gl.setRowExpandRatio(0, 0); + gl.setRowExpandRatio(1, 1); + gl.setRowExpandRatio(2, 2); + + gl.setRows(2); + assertEquals(0, gl.getRowExpandRatio(0), 0); + assertEquals(1, gl.getRowExpandRatio(1), 0); + } + + @Test + public void removeColsExpandRatiosPreserved() { + GridLayout gl = new GridLayout(3, 3); + gl.setColumnExpandRatio(0, 0); + gl.setColumnExpandRatio(1, 1); + gl.setColumnExpandRatio(2, 2); + + gl.setColumns(2); + assertEquals(0, gl.getColumnExpandRatio(0), 0); + assertEquals(1, gl.getColumnExpandRatio(1), 0); + } + private void assertContentPositions(GridLayout grid) { assertEquals(grid.getComponentCount(), children.length); int c = 0; diff --git a/shared/src/main/java/com/vaadin/shared/ui/grid/ColumnResizeMode.java b/shared/src/main/java/com/vaadin/shared/ui/grid/ColumnResizeMode.java new file mode 100644 index 0000000000..a19a2bb9eb --- /dev/null +++ b/shared/src/main/java/com/vaadin/shared/ui/grid/ColumnResizeMode.java @@ -0,0 +1,36 @@ +/* + * 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; + +/** + * Collection of modes used for resizing columns in the Grid. + */ +public enum ColumnResizeMode { + + /** + * When column resize mode is set to Animated, columns + * are resized as they are dragged. + */ + ANIMATED, + + /** + * When column resize mode is set to Simple, dragging to resize + * a column will show a marker, and the column will resize only + * after the mouse button or touch is released. + */ + SIMPLE + +} diff --git a/shared/src/main/java/com/vaadin/shared/ui/grid/GridState.java b/shared/src/main/java/com/vaadin/shared/ui/grid/GridState.java index a8abf82c75..1074b181d8 100644 --- a/shared/src/main/java/com/vaadin/shared/ui/grid/GridState.java +++ b/shared/src/main/java/com/vaadin/shared/ui/grid/GridState.java @@ -99,6 +99,11 @@ public class GridState extends AbstractSingleSelectState { primaryStyleName = "v-grid"; } + /** + * Column resize mode in grid. + */ + public ColumnResizeMode columnResizeMode = ColumnResizeMode.ANIMATED; + /** The state of the header section. */ public SectionState header = new SectionState(); diff --git a/themes/src/main/themes/VAADIN/themes/valo/components/_escalator.scss b/themes/src/main/themes/VAADIN/themes/valo/components/_escalator.scss index c91c301fa7..895d9ab975 100644 --- a/themes/src/main/themes/VAADIN/themes/valo/components/_escalator.scss +++ b/themes/src/main/themes/VAADIN/themes/valo/components/_escalator.scss @@ -75,6 +75,8 @@ } .#{$primaryStyleName}-body { + -ms-touch-action: none; + touch-action: none; z-index: 0; top: 0; diff --git a/themes/src/main/themes/VAADIN/themes/valo/components/_grid.scss b/themes/src/main/themes/VAADIN/themes/valo/components/_grid.scss index 849ff7a1e4..b0279ecea2 100644 --- a/themes/src/main/themes/VAADIN/themes/valo/components/_grid.scss +++ b/themes/src/main/themes/VAADIN/themes/valo/components/_grid.scss @@ -306,6 +306,22 @@ $v-grid-details-border-bottom-stripe: 1px solid darken($v-grid-row-background-co -ms-user-select: none; user-select: none; } + + .#{$primaryStyleName}-column-resize-simple-indicator { + position: absolute; + width: 3px; + top: 0px; + left: $v-grid-cell-padding-horizontal; + z-index: 9001; + background: #fff; + box-shadow: 0px 0px 5px #000; + + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + } // Footer diff --git a/uitest/src/main/java/com/vaadin/tests/components/grid/HorizontalScrollAfterResize.java b/uitest/src/main/java/com/vaadin/tests/components/grid/HorizontalScrollAfterResize.java new file mode 100644 index 0000000000..580f2d3a30 --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/grid/HorizontalScrollAfterResize.java @@ -0,0 +1,57 @@ +/* + * 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.grid; + +import java.util.stream.IntStream; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Grid; + +/** + * @author Vaadin Ltd + * + */ +public class HorizontalScrollAfterResize extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + final Grid<Integer> grid = new Grid<>(); + grid.setWidth("100%"); + grid.setHeight("350px"); + grid.setCaption("My Grid"); + + for (int i = 0; i < 10; i++) { + char ch = (char) ('a' + i); + grid.addColumn(item -> "test").setCaption("" + ch); + } + + grid.setItems(IntStream.of(0, 100).mapToObj(Integer::valueOf)); + + addComponents(grid); + } + + @Override + protected String getTestDescription() { + return "Don't add more than one scroll handler"; + } + + @Override + protected Integer getTicketNumber() { + return 19189; // also 20254, 19622 + } + +} 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 index c074bd4d14..e7ab23961b 100644 --- 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 @@ -22,6 +22,7 @@ import com.vaadin.event.selection.SingleSelectionEvent; import com.vaadin.server.VaadinRequest; import com.vaadin.shared.Registration; import com.vaadin.shared.data.sort.SortDirection; +import com.vaadin.shared.ui.grid.ColumnResizeMode; import com.vaadin.shared.ui.grid.HeightMode; import com.vaadin.tests.components.AbstractTestUIWithLog; import com.vaadin.ui.Button; @@ -233,6 +234,10 @@ public class GridBasics extends AbstractTestUIWithLog { .getSelectionModel()) .addSingleSelectionListener(this::onSingleSelect); + grid.addColumnResizeListener( + event -> log("ColumnResizeEvent: isUserOriginated? " + + event.isUserOriginated())); + layout.addComponent(createMenu()); layout.addComponent(grid); addComponent(layout); @@ -345,6 +350,11 @@ public class GridBasics extends AbstractTestUIWithLog { item -> grid.sort(col, SortDirection.DESCENDING)); } columnsMenu.addItem("Clear sort", item -> grid.clearSortOrder()); + + columnsMenu.addItem("Simple resize mode", + item -> grid.setColumnResizeMode(item.isChecked() + ? ColumnResizeMode.SIMPLE : ColumnResizeMode.ANIMATED)) + .setCheckable(true); } private void createSizeMenu(MenuItem sizeMenu) { diff --git a/uitest/src/main/java/com/vaadin/tests/components/textfield/TextChangeEvents.java b/uitest/src/main/java/com/vaadin/tests/components/textfield/TextChangeEvents.java new file mode 100644 index 0000000000..3db3e208af --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/textfield/TextChangeEvents.java @@ -0,0 +1,125 @@ +package com.vaadin.tests.components.textfield; + +import com.vaadin.data.HasValue; +import com.vaadin.data.HasValue.ValueChangeListener; +import com.vaadin.server.VaadinRequest; +import com.vaadin.shared.ui.ValueChangeMode; +import com.vaadin.tests.components.AbstractTestUIWithLog; +import com.vaadin.tests.util.TestUtils; +import com.vaadin.ui.AbstractTextField; +import com.vaadin.ui.TextArea; +import com.vaadin.ui.TextField; + +public class TextChangeEvents extends AbstractTestUIWithLog { + + @Override + protected void setup(VaadinRequest request) { + + TextField textField = new TextField("Default"); + + ValueChangeListener<String> listener = event -> log( + "Text change event for " + event.getComponent().getCaption() + + ", text content currently:'" + event.getValue() + + "' Cursor at index:" + + ((AbstractTextField) event.getSource()) + .getCursorPosition()); + + textField.addValueChangeListener(listener); + addComponent(textField); + + TextField eager = new TextField("Eager"); + eager.addValueChangeListener(listener); + eager.setValueChangeMode(ValueChangeMode.EAGER); + addComponent(eager); + + TextField timeout = new TextField("Timeout 3s"); + timeout.addValueChangeListener(listener); + timeout.setValueChangeMode(ValueChangeMode.EAGER); + timeout.setValueChangeTimeout(3000); + addComponent(timeout); + + TextArea textArea = new TextArea("Default text area"); + textArea.addValueChangeListener(listener); + addComponent(textArea); + + TextArea textAreaTimeout = new TextArea("Timeout 3s"); + textAreaTimeout.addValueChangeListener(listener); + textAreaTimeout.setValueChangeMode(ValueChangeMode.TIMEOUT); + textAreaTimeout.setValueChangeTimeout(3000); + addComponent(textAreaTimeout); + + VaadinDeveloperNameField vd = new VaadinDeveloperNameField(); + vd.addValueChangeListener(listener); + addComponent(vd); + } + + @Override + protected String getTestDescription() { + return "Simple TextChangeEvent test cases."; + } + + /** + * "Autosuggest" + * + * Known issue is timing if suggestion comes while typing more content. IMO + * we will not support this kind of features in default TextField, but + * hopefully make it easily extendable to perfect suggest feature. MT + * 2010-10 + * + */ + private class VaadinDeveloperNameField extends TextField + implements HasValue.ValueChangeListener<String> { + private String[] names = new String[] { "Matti Tahvonen", + "Marc Englund", "Joonas Lehtinen", "Jouni Koivuviita", + "Marko Grönroos", "Artur Signell" }; + + public VaadinDeveloperNameField() { + setCaption("Start typing 'old' Vaadin developers."); + addValueChangeListener(this); + setStyleName("nomatch"); + } + + @Override + public void attach() { + super.attach(); + TestUtils.injectCSS(getUI(), ".match { background:green ;} " + + ".nomatch {background:red;}"); + } + + @Override + public void valueChange(HasValue.ValueChangeEvent<String> event) { + boolean atTheEndOfText = event.getValue() + .length() == getCursorPosition(); + String match = findMatch(event.getValue()); + if (match != null) { + setStyleName("match"); + String curText = event.getValue(); + int matchlenght = curText.length(); + // autocomplete if garret is at the end of the text + if (atTheEndOfText) { + suggest(match, matchlenght); + } + } else { + setStyleName("nomatch"); + } + } + + private void suggest(String match, int matchlenght) { + setValue(match); + setSelection(matchlenght, match.length() - matchlenght); + } + + private String findMatch(String currentTextContent) { + if (currentTextContent.length() > 0) { + for (int i = 0; i < names.length; i++) { + if (names[i].startsWith(currentTextContent)) { + return names[i]; + } + } + } + return null; + } + + } + +} diff --git a/uitest/src/main/java/com/vaadin/v7/tests/components/grid/GridMissingProperty.java b/uitest/src/main/java/com/vaadin/v7/tests/components/grid/GridMissingProperty.java new file mode 100644 index 0000000000..99b2a53aad --- /dev/null +++ b/uitest/src/main/java/com/vaadin/v7/tests/components/grid/GridMissingProperty.java @@ -0,0 +1,129 @@ +package com.vaadin.v7.tests.components.grid; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.v7.data.Property; +import com.vaadin.v7.data.util.AbstractInMemoryContainer; +import com.vaadin.v7.data.util.BeanItem; +import com.vaadin.v7.ui.Grid; + +public class GridMissingProperty extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + final Grid grid = new Grid(); + + final Folder folder = new Folder("Folder name"); + final BeanItem<Entry> folderItem = new BeanItem<>(folder); + + final File file = new File("File name", "10kB"); + final BeanItem<Entry> fileItem = new BeanItem<>(file); + + @SuppressWarnings("unchecked") + TestContainer container = new TestContainer( + Arrays.asList(folderItem, fileItem), + Arrays.asList("name", "size")); + + grid.setContainerDataSource(container); + grid.setSelectionMode(Grid.SelectionMode.SINGLE); + grid.setEditorEnabled(true); + + addComponent(grid); + } + + @Override + protected String getTestDescription() { + return "Grid Editor should not throw exception even when items are missing properties."; + } + + private class TestContainer + extends AbstractInMemoryContainer<Object, String, BeanItem> { + + private final List<BeanItem<Entry>> items; + private final List<String> pids; + + public TestContainer(List<BeanItem<Entry>> items, List<String> pids) { + this.items = items; + this.pids = pids; + } + + @Override + protected List<Object> getAllItemIds() { + List<Object> ids = new ArrayList<>(); + for (BeanItem<Entry> item : items) { + ids.add(item.getBean()); + } + return ids; + } + + @Override + protected BeanItem<Entry> getUnfilteredItem(Object itemId) { + for (BeanItem<Entry> item : items) { + if (item.getBean().equals(itemId)) { + return item; + } + } + return null; + } + + @Override + public Collection<?> getContainerPropertyIds() { + return pids; + } + + @Override + public Property getContainerProperty(Object itemId, Object propertyId) { + return getItem(itemId).getItemProperty(propertyId); + } + + @Override + public Class<?> getType(Object propertyId) { + return String.class; + } + } + + public class Entry { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Entry(String name) { + this.name = name; + } + } + + public class Folder extends Entry { + + public Folder(String name) { + super(name); + } + } + + public class File extends Entry { + private String size; + + public File(String name, String size) { + super(name); + this.size = size; + } + + public String getSize() { + return size; + } + + public void setSize(String size) { + this.size = size; + } + } +} diff --git a/uitest/src/main/java/com/vaadin/v7/tests/components/grid/basicfeatures/GridBasicFeatures.java b/uitest/src/main/java/com/vaadin/v7/tests/components/grid/basicfeatures/GridBasicFeatures.java index 0e8f16889c..5c5acf0054 100644 --- a/uitest/src/main/java/com/vaadin/v7/tests/components/grid/basicfeatures/GridBasicFeatures.java +++ b/uitest/src/main/java/com/vaadin/v7/tests/components/grid/basicfeatures/GridBasicFeatures.java @@ -51,9 +51,10 @@ import com.vaadin.v7.data.Property.ValueChangeListener; import com.vaadin.v7.data.fieldgroup.FieldGroup.CommitException; import com.vaadin.v7.data.util.IndexedContainer; import com.vaadin.v7.event.ItemClickEvent; -import com.vaadin.v7.event.SelectionEvent; import com.vaadin.v7.event.ItemClickEvent.ItemClickListener; +import com.vaadin.v7.event.SelectionEvent; import com.vaadin.v7.event.SelectionEvent.SelectionListener; +import com.vaadin.v7.shared.ui.grid.ColumnResizeMode; import com.vaadin.v7.shared.ui.grid.GridStaticCellType; import com.vaadin.v7.shared.ui.grid.HeightMode; import com.vaadin.v7.ui.Field; @@ -1267,6 +1268,14 @@ public class GridBasicFeatures extends AbstractComponentTest<Grid> { grid.getColumns().get(0).setMaximumWidth(30); } }, null); + createBooleanAction("Simple resize mode", "Columns", false, + new Command<Grid, Boolean>() { + @Override + public void execute(Grid g, Boolean value, Object data) { + g.setColumnResizeMode(value ? ColumnResizeMode.SIMPLE + : ColumnResizeMode.ANIMATED); + } + }); } private static String getColumnProperty(int c) { diff --git a/uitest/src/main/java/com/vaadin/v7/tests/components/textfield/TextChangeEvents.java b/uitest/src/main/java/com/vaadin/v7/tests/components/textfield/TextChangeEvents.java new file mode 100644 index 0000000000..fc9ccfe04f --- /dev/null +++ b/uitest/src/main/java/com/vaadin/v7/tests/components/textfield/TextChangeEvents.java @@ -0,0 +1,136 @@ +package com.vaadin.v7.tests.components.textfield; + +import com.vaadin.tests.components.TestBase; +import com.vaadin.tests.util.Log; +import com.vaadin.tests.util.TestUtils; +import com.vaadin.v7.event.FieldEvents.TextChangeEvent; +import com.vaadin.v7.event.FieldEvents.TextChangeListener; +import com.vaadin.v7.ui.AbstractTextField.TextChangeEventMode; +import com.vaadin.v7.ui.TextArea; +import com.vaadin.v7.ui.TextField; + +public class TextChangeEvents extends TestBase { + Log l = new Log(10); + + @Override + protected void setup() { + + TextField tf = new TextField("Default"); + + TextChangeListener inputEventListener = new TextChangeListener() { + + @Override + public void textChange(TextChangeEvent event) { + l.log("Text change event for " + + event.getComponent().getCaption() + + ", text content currently:'" + event.getText() + + "' Cursor at index:" + event.getCursorPosition()); + } + }; + + tf.addListener(inputEventListener); + + getLayout().addComponent(tf); + + TextField eager = new TextField("Eager"); + eager.addListener(inputEventListener); + eager.setTextChangeEventMode(TextChangeEventMode.EAGER); + getLayout().addComponent(eager); + + TextField to = new TextField("Timeout 3s"); + to.addListener(inputEventListener); + to.setTextChangeEventMode(TextChangeEventMode.TIMEOUT); + to.setTextChangeTimeout(3000); + getLayout().addComponent(to); + + TextArea ta = new TextArea("Default text area"); + ta.addListener(inputEventListener); + getLayout().addComponent(ta); + + TextArea tat = new TextArea("Timeout 3s"); + tat.addListener(inputEventListener); + tat.setTextChangeEventMode(TextChangeEventMode.TIMEOUT); + tat.setTextChangeTimeout(3000); + getLayout().addComponent(tat); + + VaadinDeveloperNameField vd = new VaadinDeveloperNameField(); + vd.addListener(inputEventListener); + getLayout().addComponent(vd); + + getLayout().addComponent(l); + } + + @Override + protected String getDescription() { + return "Simple TextChangeEvent test cases."; + } + + @Override + protected Integer getTicketNumber() { + return null; + } + + /** + * "Autosuggest" + * + * Known issue is timing if suggestion comes while typing more content. IMO + * we will not support this kind of features in default TextField, but + * hopefully make it easily extendable to perfect suggest feature. MT + * 2010-10 + * + */ + private class VaadinDeveloperNameField extends TextField + implements TextChangeListener { + private String[] names = new String[] { "Matti Tahvonen", + "Marc Englund", "Joonas Lehtinen", "Jouni Koivuviita", + "Marko Grönroos", "Artur Signell" }; + + public VaadinDeveloperNameField() { + setCaption("Start typing 'old' Vaadin developers."); + addListener((TextChangeListener) this); + setStyleName("nomatch"); + } + + @Override + public void attach() { + super.attach(); + TestUtils.injectCSS(getUI(), ".match { background:green ;} " + + ".nomatch {background:red;}"); + } + + @Override + public void textChange(TextChangeEvent event) { + boolean atTheEndOfText = event.getText() + .length() == getCursorPosition(); + String match = findMatch(event.getText()); + if (match != null) { + setStyleName("match"); + String curText = event.getText(); + int matchlenght = curText.length(); + // autocomplete if garret is at the end of the text + if (atTheEndOfText) { + suggest(match, matchlenght); + } + } else { + setStyleName("nomatch"); + } + } + + private void suggest(String match, int matchlenght) { + setValue(match); + setSelectionRange(matchlenght, match.length() - matchlenght); + } + + private String findMatch(String currentTextContent) { + if (currentTextContent.length() > 0) { + for (int i = 0; i < names.length; i++) { + if (names[i].startsWith(currentTextContent)) { + return names[i]; + } + } + } + return null; + } + } + +} diff --git a/uitest/src/test/java/com/vaadin/tests/components/grid/HorizontalScrollAfterResizeTest.java b/uitest/src/test/java/com/vaadin/tests/components/grid/HorizontalScrollAfterResizeTest.java new file mode 100644 index 0000000000..fcc9681b61 --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/grid/HorizontalScrollAfterResizeTest.java @@ -0,0 +1,68 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.grid; + +import org.junit.Assert; +import org.junit.Test; +import org.openqa.selenium.Dimension; +import org.openqa.selenium.Point; + +import com.vaadin.testbench.elements.GridElement; +import com.vaadin.v7.tests.components.grid.basicfeatures.GridBasicFeaturesTest; + +/** + * @author Vaadin Ltd + * + */ +public class HorizontalScrollAfterResizeTest extends GridBasicFeaturesTest { + + /** + * The behavior without the fix differs across different browsers but + * scenario should work everywhere. + */ + @Test + public void scrollAfterResize() { + getDriver().manage().window().setSize(new Dimension(600, 400)); + openTestURL(); + getDriver().manage().window().setSize(new Dimension(200, 400)); + + // First scroll to the right + scrollGridHorizontallyTo(600); + Point locationAfterFirstScroll = $(GridElement.class).first() + .getCell(0, 9).getLocation(); + + // resize back + getDriver().manage().window().setSize(new Dimension(600, 400)); + // shrink again + getDriver().manage().window().setSize(new Dimension(200, 400)); + + // second scroll to the right + scrollGridHorizontallyTo(600); + + Point lolocationAfterSecondScrollcation = $(GridElement.class).first() + .getCell(0, 9).getLocation(); + + // With the bug scrolling doesn't happen. Location should be the same as + // first time + Assert.assertEquals(locationAfterFirstScroll, + lolocationAfterSecondScrollcation); + } + + @Override + protected Class<?> getUIClass() { + return HorizontalScrollAfterResize.class; + } +} diff --git a/uitest/src/test/java/com/vaadin/tests/components/grid/basicfeatures/GridColumnResizeModeTest.java b/uitest/src/test/java/com/vaadin/tests/components/grid/basicfeatures/GridColumnResizeModeTest.java new file mode 100644 index 0000000000..f92366cdb4 --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/grid/basicfeatures/GridColumnResizeModeTest.java @@ -0,0 +1,82 @@ +/* + * 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.grid.basicfeatures; + +import java.util.List; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.interactions.Actions; + +import com.vaadin.testbench.By; +import com.vaadin.testbench.customelements.GridElement; +import com.vaadin.testbench.parallel.TestCategory; +import com.vaadin.tests.components.grid.basics.GridBasicsTest; + +@TestCategory("grid") +public class GridColumnResizeModeTest extends GridBasicsTest { + + @Before + public void before() { + openTestURL(); + } + + @Test + public void testSimpleResizeModeToggle() throws Exception { + + GridElement grid = getGridElement(); + + List<WebElement> handles = grid + .findElements(By.className("v-grid-column-resize-handle")); + WebElement handle = handles.get(1); + + Actions drag1 = new Actions(getDriver()).moveToElement(handle) + .clickAndHold(); + Actions drag2 = new Actions(getDriver()).moveByOffset(-50, 0); + Actions drag3 = new Actions(getDriver()).moveByOffset(100, 0); + Actions dragEndAction = new Actions(getDriver()).release() + .moveToElement(grid); + + selectMenuPath("Component", "Columns", "Simple resize mode"); + sleep(250); + + drag1.perform(); + sleep(500); + drag2.perform(); + sleep(500); + drag3.perform(); + sleep(500); + + // Make sure we find at least one simple resize mode splitter + assertElementPresent( + By.className("v-grid-column-resize-simple-indicator")); + + dragEndAction.perform(); + + // Make sure it went away + assertElementNotPresent( + By.className("v-grid-column-resize-simple-indicator")); + + // See that we got a resize event + sleep(500); + Assert.assertTrue("Log shows resize event", getLogRow(0) + .contains("ColumnResizeEvent: isUserOriginated? true")); + + } + +} diff --git a/uitest/src/test/java/com/vaadin/tests/components/textfield/TextChangeEventsTest.java b/uitest/src/test/java/com/vaadin/tests/components/textfield/TextChangeEventsTest.java new file mode 100644 index 0000000000..5693400eba --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/textfield/TextChangeEventsTest.java @@ -0,0 +1,50 @@ +package com.vaadin.tests.components.textfield; + +import org.junit.Test; + +import com.vaadin.testbench.elements.TextAreaElement; +import com.vaadin.testbench.elements.TextFieldElement; +import com.vaadin.tests.tb3.SingleBrowserTest; + +public class TextChangeEventsTest extends SingleBrowserTest { + + @Test + public void textAreaWaitsForTextChangeEvents() { + openTestURL(); + + TextAreaElement taDefault = $(TextAreaElement.class) + .caption("Default text area").first(); + taDefault.sendKeys("abc"); + waitUntil(driver -> getLogRow(0).equals( + "1. Text change event for Default text area, text content currently:'abc' Cursor at index:3")); + + TextAreaElement taTimeout = $(TextAreaElement.class) + .caption("Timeout 3s").first(); + taTimeout.sendKeys("abc"); + waitUntil(driver -> getLogRow(0).equals( + "2. Text change event for Timeout 3s, text content currently:'abc' Cursor at index:3")); + } + + @Test + public void textFieldWaitsForTextChangeEvents() { + openTestURL(); + + TextFieldElement tfDefault = $(TextFieldElement.class) + .caption("Default").first(); + tfDefault.sendKeys("abc"); + waitUntil(driver -> getLogRow(0).equals( + "1. Text change event for Default, text content currently:'abc' Cursor at index:3")); + + TextFieldElement tfEager = $(TextFieldElement.class).caption("Eager") + .first(); + tfEager.sendKeys("abc"); + waitUntil(driver -> getLogRow(0).contains( + "Text change event for Eager, text content currently:'abc' Cursor at index:3")); + + TextFieldElement tfTimeout = $(TextFieldElement.class) + .caption("Timeout 3s").first(); + tfTimeout.sendKeys("abc"); + waitUntil(driver -> getLogRow(0).contains( + "Text change event for Timeout 3s, text content currently:'abc' Cursor at index:3")); + } +} diff --git a/uitest/src/test/java/com/vaadin/v7/tests/components/grid/GridMissingPropertyTest.java b/uitest/src/test/java/com/vaadin/v7/tests/components/grid/GridMissingPropertyTest.java new file mode 100644 index 0000000000..88262dcfcc --- /dev/null +++ b/uitest/src/test/java/com/vaadin/v7/tests/components/grid/GridMissingPropertyTest.java @@ -0,0 +1,73 @@ +package com.vaadin.v7.tests.components.grid; + +import org.junit.Test; + +import com.vaadin.testbench.elements.GridElement; +import com.vaadin.testbench.elements.GridElement.GridEditorElement; +import com.vaadin.testbench.elements.TextFieldElement; +import com.vaadin.tests.tb3.SingleBrowserTest; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class GridMissingPropertyTest extends SingleBrowserTest { + + @Test + public void testCellEditable() { + openTestURL(); + GridElement grid = $(GridElement.class).first(); + + // Row with missing property + grid.getCell(0, 0).doubleClick(); + GridEditorElement editor = grid.getEditor(); + + assertTrue("Cell with property should be editable", + editor.isEditable(0)); + assertFalse("Cell without property should not be editable", + editor.isEditable(1)); + + editor.cancel(); + + // Row with all properties + grid.getCell(1, 0).doubleClick(); + editor = grid.getEditor(); + + assertTrue("Cell with property should be editable", + editor.isEditable(0)); + assertTrue("Cell with property should be editable", + editor.isEditable(1)); + + editor.cancel(); + } + + @Test + public void testEditCell() { + openTestURL(); + GridElement grid = $(GridElement.class).first(); + + GridEditorElement editor; + TextFieldElement editorField; + + grid.getCell(0, 0).doubleClick(); + editor = grid.getEditor(); + editorField = editor.getField(0).wrap(TextFieldElement.class); + editorField.setValue("New Folder Name"); + editor.save(); + assertEquals("New Folder Name", grid.getCell(0, 0).getText()); + + grid.getCell(1, 0).doubleClick(); + editor = grid.getEditor(); + editorField = editor.getField(1).wrap(TextFieldElement.class); + editorField.setValue("10 MB"); + editor.save(); + assertEquals("10 MB", grid.getCell(1, 1).getText()); + + grid.getCell(1, 0).doubleClick(); + editor = grid.getEditor(); + editorField = editor.getField(0).wrap(TextFieldElement.class); + editorField.setValue("New File Name"); + editor.save(); + assertEquals("New File Name", grid.getCell(1, 0).getText()); + } +} diff --git a/uitest/src/test/java/com/vaadin/v7/tests/components/grid/basicfeatures/GridColumnResizeModeTest.java b/uitest/src/test/java/com/vaadin/v7/tests/components/grid/basicfeatures/GridColumnResizeModeTest.java new file mode 100644 index 0000000000..f287a2aaa2 --- /dev/null +++ b/uitest/src/test/java/com/vaadin/v7/tests/components/grid/basicfeatures/GridColumnResizeModeTest.java @@ -0,0 +1,81 @@ +/* + * 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.v7.tests.components.grid.basicfeatures; + +import java.util.List; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.interactions.Actions; + +import com.vaadin.testbench.By; +import com.vaadin.testbench.customelements.GridElement; +import com.vaadin.testbench.parallel.TestCategory; + +@TestCategory("grid") +public class GridColumnResizeModeTest extends GridBasicFeaturesTest { + + @Before + public void before() { + openTestURL(); + } + + @Test + public void testSimpleResizeModeToggle() throws Exception { + + GridElement grid = getGridElement(); + + List<WebElement> handles = grid + .findElements(By.className("v-grid-column-resize-handle")); + WebElement handle = handles.get(1); + + Actions drag1 = new Actions(getDriver()).moveToElement(handle) + .clickAndHold(); + Actions drag2 = new Actions(getDriver()).moveByOffset(-50, 0); + Actions drag3 = new Actions(getDriver()).moveByOffset(100, 0); + Actions dragEndAction = new Actions(getDriver()).release() + .moveToElement(grid); + + selectMenuPath("Component", "Columns", "Simple resize mode"); + sleep(250); + + drag1.perform(); + sleep(500); + drag2.perform(); + sleep(500); + drag3.perform(); + sleep(500); + + // Make sure we find at least one simple resize mode splitter + assertElementPresent( + By.className("v-grid-column-resize-simple-indicator")); + + dragEndAction.perform(); + + // Make sure it went away + assertElementNotPresent( + By.className("v-grid-column-resize-simple-indicator")); + + // See that we got a resize event + sleep(500); + Assert.assertEquals("Log shows resize event", getLogRow(0), + "3. ColumnResizeEvent: isUserOriginated? true"); + + } + +}
\ No newline at end of file diff --git a/uitest/src/test/java/com/vaadin/v7/tests/components/textfield/TextChangeEventsTest.java b/uitest/src/test/java/com/vaadin/v7/tests/components/textfield/TextChangeEventsTest.java new file mode 100644 index 0000000000..c6880fc700 --- /dev/null +++ b/uitest/src/test/java/com/vaadin/v7/tests/components/textfield/TextChangeEventsTest.java @@ -0,0 +1,58 @@ +package com.vaadin.v7.tests.components.textfield; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.testbench.elements.TextAreaElement; +import com.vaadin.testbench.elements.TextFieldElement; +import com.vaadin.tests.tb3.SingleBrowserTest; + +public class TextChangeEventsTest extends SingleBrowserTest { + + @Test + public void textAreaWaitsForTextChangeEvents() { + openTestURL(); + + TextAreaElement taDefault = $(TextAreaElement.class) + .caption("Default text area").first(); + taDefault.sendKeys("abc"); + Assert.assertEquals( + "1. Text change event for Default text area, text content currently:'abc' Cursor at index:3", + getLogRow(0)); + + TextAreaElement taTimeout = $(TextAreaElement.class) + .caption("Timeout 3s").first(); + taTimeout.sendKeys("abc"); + Assert.assertEquals( + "2. Text change event for Timeout 3s, text content currently:'abc' Cursor at index:3", + getLogRow(0)); + + } + + @Test + public void textFieldWaitsForTextChangeEvents() { + openTestURL(); + + TextFieldElement tfDefault = $(TextFieldElement.class) + .caption("Default").first(); + tfDefault.sendKeys("abc"); + Assert.assertEquals( + "1. Text change event for Default, text content currently:'abc' Cursor at index:3", + getLogRow(0)); + + TextFieldElement tfEager = $(TextFieldElement.class).caption("Eager") + .first(); + tfEager.sendKeys("abc"); + Assert.assertEquals( + "2. Text change event for Eager, text content currently:'abc' Cursor at index:3", + getLogRow(0)); + + TextFieldElement tfTimeout = $(TextFieldElement.class) + .caption("Timeout 3s").first(); + tfTimeout.sendKeys("abc"); + Assert.assertEquals( + "3. Text change event for Timeout 3s, text content currently:'abc' Cursor at index:3", + getLogRow(0)); + + } +}
\ No newline at end of file |