Change-Id: Ie1f9d738db7a03ddb01b968782ad5e4877af1d7etags/7.5.0.alpha1
max-width: 100%; | max-width: 100%; | ||||
} | } | ||||
} | } | ||||
.error::before { | |||||
position: absolute; | |||||
display: block; | |||||
height: 0; | |||||
width: 0; | |||||
content: ""; | |||||
border-top: 5px solid red; | |||||
border-right: 5px solid transparent; | |||||
} | |||||
.error, | |||||
.error > input { | |||||
background-color: #fee; | |||||
} | |||||
} | } | ||||
.#{$primaryStyleName}-editor-footer { | .#{$primaryStyleName}-editor-footer { |
height: 100%; | height: 100%; | ||||
vertical-align: middle; | vertical-align: middle; | ||||
} | } | ||||
.error::before { | |||||
border-top: round($v-unit-size / 4) solid $v-error-indicator-color; | |||||
border-right: round($v-unit-size / 4) solid transparent; | |||||
} | |||||
.error, | |||||
.error > input { | |||||
// taken from @mixin valo-textfield-error-style() | |||||
background-color: scale-color($v-error-indicator-color, $lightness: 98%); | |||||
} | |||||
.v-textfield, | .v-textfield, | ||||
.v-textfield-focus, | .v-textfield-focus, |
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.Arrays; | import java.util.Arrays; | ||||
import java.util.Collection; | |||||
import java.util.HashMap; | import java.util.HashMap; | ||||
import java.util.HashSet; | import java.util.HashSet; | ||||
import java.util.Iterator; | import java.util.Iterator; | ||||
private EditorServerRpc rpc = getRpcProxy(EditorServerRpc.class); | private EditorServerRpc rpc = getRpcProxy(EditorServerRpc.class); | ||||
private EditorRequest<?> currentRequest = null; | |||||
private EditorRequest<JsonObject> currentRequest = null; | |||||
private boolean serverInitiated = false; | private boolean serverInitiated = false; | ||||
public CustomEditorHandler() { | public CustomEditorHandler() { | ||||
@Override | @Override | ||||
public void confirmBind(final boolean bindSucceeded) { | public void confirmBind(final boolean bindSucceeded) { | ||||
endRequest(bindSucceeded); | |||||
endRequest(bindSucceeded, null); | |||||
} | } | ||||
@Override | @Override | ||||
public void confirmSave(boolean saveSucceeded) { | |||||
endRequest(saveSucceeded); | |||||
public void confirmSave(boolean saveSucceeded, | |||||
List<String> errorColumnsIds) { | |||||
endRequest(saveSucceeded, errorColumnsIds); | |||||
} | } | ||||
}); | }); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
private void startRequest(EditorRequest<?> request) { | |||||
private void startRequest(EditorRequest<JsonObject> request) { | |||||
assert currentRequest == null : "Earlier request not yet finished"; | assert currentRequest == null : "Earlier request not yet finished"; | ||||
currentRequest = request; | currentRequest = request; | ||||
} | } | ||||
private void endRequest(boolean succeeded) { | |||||
private void endRequest(boolean succeeded, List<String> errorColumnsIds) { | |||||
assert currentRequest != null : "Current request was null"; | assert currentRequest != null : "Current request was null"; | ||||
/* | /* | ||||
* Clear current request first to ensure the state is valid if | * Clear current request first to ensure the state is valid if | ||||
* another request is made in the callback. | * another request is made in the callback. | ||||
*/ | */ | ||||
EditorRequest<?> request = currentRequest; | |||||
EditorRequest<JsonObject> request = currentRequest; | |||||
currentRequest = null; | currentRequest = null; | ||||
if (succeeded) { | if (succeeded) { | ||||
request.success(); | request.success(); | ||||
} else { | } else { | ||||
request.fail(); | |||||
Collection<Column<?, JsonObject>> errorColumns; | |||||
if (errorColumnsIds != null) { | |||||
errorColumns = new ArrayList<Grid.Column<?, JsonObject>>(); | |||||
for (String colId : errorColumnsIds) { | |||||
errorColumns.add(columnIdToColumn.get(colId)); | |||||
} | |||||
} else { | |||||
errorColumns = null; | |||||
} | |||||
request.failure(errorColumns); | |||||
} | } | ||||
} | } | ||||
} | } |
*/ | */ | ||||
package com.vaadin.client.widget.grid; | package com.vaadin.client.widget.grid; | ||||
import java.util.Collection; | |||||
import com.google.gwt.user.client.ui.Widget; | import com.google.gwt.user.client.ui.Widget; | ||||
import com.vaadin.client.widgets.Grid; | import com.vaadin.client.widgets.Grid; | ||||
* @param <T> | * @param <T> | ||||
* the row data type | * the row data type | ||||
*/ | */ | ||||
public static class EditorRequest<T> { | |||||
/** | |||||
* A callback interface used to notify the invoker of the editor handler | |||||
* of completed editor requests. | |||||
* | |||||
* @param <T> | |||||
* the row data type | |||||
*/ | |||||
public interface RequestCallback<T> { | |||||
/** | |||||
* The method that must be called when the request has been | |||||
* processed correctly. | |||||
* | |||||
* @param request | |||||
* the original request object | |||||
*/ | |||||
public void onSuccess(EditorRequest<T> request); | |||||
/** | |||||
* The method that must be called when processing the request has | |||||
* produced an aborting error. | |||||
* | |||||
* @param request | |||||
* the original request object | |||||
*/ | |||||
public void onError(EditorRequest<T> request); | |||||
} | |||||
private Grid<T> grid; | |||||
private int rowIndex; | |||||
private RequestCallback<T> callback; | |||||
private boolean completed = false; | |||||
/** | |||||
* Creates a new editor request. | |||||
* | |||||
* @param rowIndex | |||||
* the index of the edited row | |||||
* @param callback | |||||
* the callback invoked when the request is ready, or null if | |||||
* no need to call back | |||||
*/ | |||||
public EditorRequest(Grid<T> grid, int rowIndex, | |||||
RequestCallback<T> callback) { | |||||
this.grid = grid; | |||||
this.rowIndex = rowIndex; | |||||
this.callback = callback; | |||||
} | |||||
public interface EditorRequest<T> { | |||||
/** | /** | ||||
* Returns the index of the row being requested. | * Returns the index of the row being requested. | ||||
* | * | ||||
* @return the row index | * @return the row index | ||||
*/ | */ | ||||
public int getRowIndex() { | |||||
return rowIndex; | |||||
} | |||||
public int getRowIndex(); | |||||
/** | /** | ||||
* Returns the row data related to the row being requested. | * Returns the row data related to the row being requested. | ||||
* | * | ||||
* @return the row data | * @return the row data | ||||
*/ | */ | ||||
public T getRow() { | |||||
return grid.getDataSource().getRow(rowIndex); | |||||
} | |||||
public T getRow(); | |||||
/** | /** | ||||
* Returns the grid instance related to this editor request. | * Returns the grid instance related to this editor request. | ||||
* | * | ||||
* @return the grid instance | * @return the grid instance | ||||
*/ | */ | ||||
public Grid<T> getGrid() { | |||||
return grid; | |||||
} | |||||
public Grid<T> getGrid(); | |||||
/** | /** | ||||
* Returns the editor widget used to edit the values of the given | * Returns the editor widget used to edit the values of the given | ||||
* the column whose widget to get | * the column whose widget to get | ||||
* @return the widget related to the column | * @return the widget related to the column | ||||
*/ | */ | ||||
public Widget getWidget(Grid.Column<?, T> column) { | |||||
Widget w = grid.getEditorWidget(column); | |||||
assert w != null; | |||||
return w; | |||||
} | |||||
/** | |||||
* Completes this request. The request can only be completed once. This | |||||
* method should only be called by an EditorHandler implementer if the | |||||
* request handling is asynchronous in nature and {@link #startAsync()} | |||||
* is previously invoked for this request. Synchronous requests are | |||||
* completed automatically by the editor. | |||||
* | |||||
* @throws IllegalStateException | |||||
* if the request is already completed | |||||
*/ | |||||
private void complete() { | |||||
if (completed) { | |||||
throw new IllegalStateException( | |||||
"An EditorRequest must be completed exactly once"); | |||||
} | |||||
completed = true; | |||||
} | |||||
public Widget getWidget(Grid.Column<?, T> column); | |||||
/** | /** | ||||
* Informs Grid that the editor request was a success. | * Informs Grid that the editor request was a success. | ||||
*/ | */ | ||||
public void success() { | |||||
complete(); | |||||
if (callback != null) { | |||||
callback.onSuccess(this); | |||||
} | |||||
} | |||||
public void success(); | |||||
/** | /** | ||||
* Informs Grid that an error occurred while trying to process the | * Informs Grid that an error occurred while trying to process the | ||||
* request. | * request. | ||||
* | |||||
* @param errorColumns | |||||
* a collection of columns for which an error indicator | |||||
* should be shown, or <code>null</code> if no columns should | |||||
* be marked as erroneous. | |||||
*/ | */ | ||||
public void fail() { | |||||
complete(); | |||||
if (callback != null) { | |||||
callback.onError(this); | |||||
} | |||||
} | |||||
public void failure(Collection<Grid.Column<?, T>> errorColumns); | |||||
/** | /** | ||||
* Checks whether the request is completed or not. | * Checks whether the request is completed or not. | ||||
* | * | ||||
* @return <code>true</code> iff the request is completed | * @return <code>true</code> iff the request is completed | ||||
*/ | */ | ||||
public boolean isCompleted() { | |||||
return completed; | |||||
} | |||||
public boolean isCompleted(); | |||||
} | } | ||||
/** | /** | ||||
* opened for editing. | * opened for editing. | ||||
* <p> | * <p> | ||||
* The implementation <em>must</em> call either | * The implementation <em>must</em> call either | ||||
* {@link EditorRequest#success()} or {@link EditorRequest#fail()} to signal | |||||
* a successful or a failed (respectively) bind action. | |||||
* {@link EditorRequest#success()} or | |||||
* {@link EditorRequest#failure(Collection)} to signal a successful or a | |||||
* failed (respectively) bind action. | |||||
* | * | ||||
* @param request | * @param request | ||||
* the data binding request | * the data binding request |
import com.vaadin.client.widget.grid.DataAvailableHandler; | import com.vaadin.client.widget.grid.DataAvailableHandler; | ||||
import com.vaadin.client.widget.grid.EditorHandler; | import com.vaadin.client.widget.grid.EditorHandler; | ||||
import com.vaadin.client.widget.grid.EditorHandler.EditorRequest; | import com.vaadin.client.widget.grid.EditorHandler.EditorRequest; | ||||
import com.vaadin.client.widget.grid.EditorHandler.EditorRequest.RequestCallback; | |||||
import com.vaadin.client.widget.grid.EventCellReference; | import com.vaadin.client.widget.grid.EventCellReference; | ||||
import com.vaadin.client.widget.grid.RendererCellReference; | import com.vaadin.client.widget.grid.RendererCellReference; | ||||
import com.vaadin.client.widget.grid.RowReference; | import com.vaadin.client.widget.grid.RowReference; | ||||
} | } | ||||
} | } | ||||
private static class EditorRequestImpl<T> implements EditorRequest<T> { | |||||
/** | |||||
* A callback interface used to notify the invoker of the editor handler | |||||
* of completed editor requests. | |||||
* | |||||
* @param <T> | |||||
* the row data type | |||||
*/ | |||||
public static interface RequestCallback<T> { | |||||
/** | |||||
* The method that must be called when the request has been | |||||
* processed correctly. | |||||
* | |||||
* @param request | |||||
* the original request object | |||||
*/ | |||||
public void onSuccess(EditorRequest<T> request); | |||||
/** | |||||
* The method that must be called when processing the request has | |||||
* produced an aborting error. | |||||
* | |||||
* @param request | |||||
* the original request object | |||||
*/ | |||||
public void onError(EditorRequest<T> request); | |||||
} | |||||
private Grid<T> grid; | |||||
private int rowIndex; | |||||
private RequestCallback<T> callback; | |||||
private boolean completed = false; | |||||
public EditorRequestImpl(Grid<T> grid, int rowIndex, | |||||
RequestCallback<T> callback) { | |||||
this.grid = grid; | |||||
this.rowIndex = rowIndex; | |||||
this.callback = callback; | |||||
} | |||||
@Override | |||||
public int getRowIndex() { | |||||
return rowIndex; | |||||
} | |||||
@Override | |||||
public T getRow() { | |||||
return grid.getDataSource().getRow(rowIndex); | |||||
} | |||||
@Override | |||||
public Grid<T> getGrid() { | |||||
return grid; | |||||
} | |||||
@Override | |||||
public Widget getWidget(Grid.Column<?, T> column) { | |||||
Widget w = grid.getEditorWidget(column); | |||||
assert w != null; | |||||
return w; | |||||
} | |||||
private void complete(Collection<Column<?, T>> errorColumns) { | |||||
if (completed) { | |||||
throw new IllegalStateException( | |||||
"An EditorRequest must be completed exactly once"); | |||||
} | |||||
completed = true; | |||||
grid.getEditor().clearEditorColumnErrors(); | |||||
if (errorColumns != null) { | |||||
for (Column<?, T> column : errorColumns) { | |||||
grid.getEditor().setEditorColumnError(column, true); | |||||
} | |||||
} | |||||
} | |||||
@Override | |||||
public void success() { | |||||
complete(null); | |||||
if (callback != null) { | |||||
callback.onSuccess(this); | |||||
} | |||||
} | |||||
@Override | |||||
public void failure(Collection<Grid.Column<?, T>> errorColumns) { | |||||
complete(errorColumns); | |||||
if (callback != null) { | |||||
callback.onError(this); | |||||
} | |||||
} | |||||
@Override | |||||
public boolean isCompleted() { | |||||
return completed; | |||||
} | |||||
} | |||||
/** | /** | ||||
* An editor UI for Grid rows. A single Grid row at a time can be opened for | * An editor UI for Grid rows. A single Grid row at a time can be opened for | ||||
* editing. | * editing. | ||||
public static final int KEYCODE_SHOW = KeyCodes.KEY_ENTER; | public static final int KEYCODE_SHOW = KeyCodes.KEY_ENTER; | ||||
public static final int KEYCODE_HIDE = KeyCodes.KEY_ESCAPE; | public static final int KEYCODE_HIDE = KeyCodes.KEY_ESCAPE; | ||||
private static final String ERROR_CLASS_NAME = "error"; | |||||
protected enum State { | protected enum State { | ||||
INACTIVE, ACTIVATING, BINDING, ACTIVE, SAVING | INACTIVE, ACTIVATING, BINDING, ACTIVE, SAVING | ||||
} | } | ||||
} | } | ||||
}; | }; | ||||
private final RequestCallback<T> saveRequestCallback = new RequestCallback<T>() { | |||||
private final EditorRequestImpl.RequestCallback<T> saveRequestCallback = new EditorRequestImpl.RequestCallback<T>() { | |||||
@Override | @Override | ||||
public void onSuccess(EditorRequest<T> request) { | public void onSuccess(EditorRequest<T> request) { | ||||
if (state == State.SAVING) { | if (state == State.SAVING) { | ||||
+ " remember to call success() or fail()?"); | + " remember to call success() or fail()?"); | ||||
} | } | ||||
}; | }; | ||||
private final RequestCallback<T> bindRequestCallback = new RequestCallback<T>() { | |||||
private final EditorRequestImpl.RequestCallback<T> bindRequestCallback = new EditorRequestImpl.RequestCallback<T>() { | |||||
@Override | @Override | ||||
public void onSuccess(EditorRequest<T> request) { | public void onSuccess(EditorRequest<T> request) { | ||||
if (state == State.BINDING) { | if (state == State.BINDING) { | ||||
} | } | ||||
}; | }; | ||||
/** A set of all the columns that display an error flag. */ | |||||
private final Set<Column<?, T>> columnErrors = new HashSet<Grid.Column<?, T>>(); | |||||
public Editor() { | public Editor() { | ||||
saveButton = new Button(); | saveButton = new Button(); | ||||
saveButton.setText(GridConstants.DEFAULT_SAVE_CAPTION); | saveButton.setText(GridConstants.DEFAULT_SAVE_CAPTION); | ||||
hideOverlay(); | hideOverlay(); | ||||
grid.getEscalator().setScrollLocked(Direction.VERTICAL, false); | grid.getEscalator().setScrollLocked(Direction.VERTICAL, false); | ||||
EditorRequest<T> request = new EditorRequest<T>(grid, rowIndex, | |||||
EditorRequest<T> request = new EditorRequestImpl<T>(grid, rowIndex, | |||||
null); | null); | ||||
handler.cancel(request); | handler.cancel(request); | ||||
state = State.INACTIVE; | state = State.INACTIVE; | ||||
state = State.SAVING; | state = State.SAVING; | ||||
setButtonsEnabled(false); | setButtonsEnabled(false); | ||||
saveTimeout.schedule(SAVE_TIMEOUT_MS); | saveTimeout.schedule(SAVE_TIMEOUT_MS); | ||||
EditorRequest<T> request = new EditorRequest<T>(grid, rowIndex, | |||||
EditorRequest<T> request = new EditorRequestImpl<T>(grid, rowIndex, | |||||
saveRequestCallback); | saveRequestCallback); | ||||
handler.save(request); | handler.save(request); | ||||
} | } | ||||
if (state == State.ACTIVATING) { | if (state == State.ACTIVATING) { | ||||
state = State.BINDING; | state = State.BINDING; | ||||
bindTimeout.schedule(BIND_TIMEOUT_MS); | bindTimeout.schedule(BIND_TIMEOUT_MS); | ||||
EditorRequest<T> request = new EditorRequest<T>(grid, rowIndex, | |||||
bindRequestCallback); | |||||
EditorRequest<T> request = new EditorRequestImpl<T>(grid, | |||||
rowIndex, bindRequestCallback); | |||||
handler.bind(request); | handler.bind(request); | ||||
grid.getEscalator().setScrollLocked(Direction.VERTICAL, true); | grid.getEscalator().setScrollLocked(Direction.VERTICAL, true); | ||||
} | } | ||||
editorOverlay.removeFromParent(); | editorOverlay.removeFromParent(); | ||||
scrollHandler.removeHandler(); | scrollHandler.removeHandler(); | ||||
clearEditorColumnErrors(); | |||||
} | } | ||||
protected void setStylePrimaryName(String primaryName) { | protected void setStylePrimaryName(String primaryName) { | ||||
public String getCancelCaption() { | public String getCancelCaption() { | ||||
return cancelButton.getText(); | return cancelButton.getText(); | ||||
} | } | ||||
public void setEditorColumnError(Column<?, T> column, boolean hasError) { | |||||
if (state != State.ACTIVE && state != State.SAVING) { | |||||
throw new IllegalStateException("Cannot set cell error " | |||||
+ "status: editor is neither active nor saving."); | |||||
} | |||||
if (isEditorColumnError(column) == hasError) { | |||||
return; | |||||
} | |||||
Element editorCell = getWidget(column).getElement() | |||||
.getParentElement(); | |||||
if (hasError) { | |||||
editorCell.addClassName(ERROR_CLASS_NAME); | |||||
columnErrors.add(column); | |||||
} else { | |||||
editorCell.removeClassName(ERROR_CLASS_NAME); | |||||
columnErrors.remove(column); | |||||
} | |||||
} | |||||
public void clearEditorColumnErrors() { | |||||
/* | |||||
* editorOverlay has no children if it's not active, effectively | |||||
* making this loop a NOOP. | |||||
*/ | |||||
Element e = editorOverlay.getFirstChildElement(); | |||||
while (e != null) { | |||||
e.removeClassName(ERROR_CLASS_NAME); | |||||
e = e.getNextSiblingElement(); | |||||
} | |||||
columnErrors.clear(); | |||||
} | |||||
public boolean isEditorColumnError(Column<?, T> column) { | |||||
return columnErrors.contains(column); | |||||
} | |||||
} | } | ||||
public static abstract class AbstractGridKeyEvent<HANDLER extends AbstractGridKeyEventHandler> | public static abstract class AbstractGridKeyEvent<HANDLER extends AbstractGridKeyEventHandler> | ||||
* | * | ||||
* @return {@code true} if this column is editable, {@code false} | * @return {@code true} if this column is editable, {@code false} | ||||
* otherwise | * otherwise | ||||
* | |||||
* | |||||
* @see #setEditable(boolean) | * @see #setEditable(boolean) | ||||
*/ | */ | ||||
public boolean isEditable() { | public boolean isEditable() { |
private CommitException cause; | private CommitException cause; | ||||
private Set<Column> errorColumns = new HashSet<Column>(); | |||||
public CommitErrorEvent(Grid grid, CommitException cause) { | public CommitErrorEvent(Grid grid, CommitException cause) { | ||||
super(grid); | super(grid); | ||||
this.cause = cause; | this.cause = cause; | ||||
return cause.getCause() instanceof InvalidValueException; | return cause.getCause() instanceof InvalidValueException; | ||||
} | } | ||||
/** | |||||
* Marks that an error indicator should be shown for the editor of a | |||||
* column. | |||||
* | |||||
* @param column | |||||
* the column to show an error for | |||||
*/ | |||||
public void addErrorColumn(Column column) { | |||||
errorColumns.add(column); | |||||
} | |||||
/** | |||||
* Gets all the columns that have been marked as erroneous. | |||||
* | |||||
* @return an umodifiable collection of erroneous columns | |||||
*/ | |||||
public Collection<Column> getErrorColumns() { | |||||
return Collections.unmodifiableCollection(errorColumns); | |||||
} | |||||
} | } | ||||
/** | /** | ||||
.getCause().getInvalidFields(); | .getCause().getInvalidFields(); | ||||
if (!invalidFields.isEmpty()) { | if (!invalidFields.isEmpty()) { | ||||
// Validation error, show first failure as | |||||
// "<Column header>: <message>" | |||||
Object firstErrorPropertyId = null; | |||||
Field<?> firstErrorField = null; | |||||
FieldGroup fieldGroup = event.getCause().getFieldGroup(); | FieldGroup fieldGroup = event.getCause().getFieldGroup(); | ||||
Object propertyId = getFirstPropertyId(fieldGroup, | |||||
invalidFields.keySet()); | |||||
Field<?> field = fieldGroup.getField(propertyId); | |||||
String caption = getColumn(propertyId).getHeaderCaption(); | |||||
// TODO This should be shown in the editor component once | |||||
// there is a place for that. Optionally, all errors should be | |||||
// shown | |||||
Notification.show(caption + ": " | |||||
+ invalidFields.get(field).getLocalizedMessage(), | |||||
Type.ERROR_MESSAGE); | |||||
for (Column column : getColumns()) { | |||||
Object propertyId = column.getPropertyId(); | |||||
Field<?> field = fieldGroup.getField(propertyId); | |||||
if (invalidFields.keySet().contains(field)) { | |||||
event.addErrorColumn(column); | |||||
if (firstErrorPropertyId == null) { | |||||
firstErrorPropertyId = propertyId; | |||||
firstErrorField = field; | |||||
} | |||||
} | |||||
} | |||||
/* | |||||
* Validation error, show first failure as | |||||
* "<Column header>: <message>" | |||||
*/ | |||||
String caption = getColumn(firstErrorPropertyId) | |||||
.getHeaderCaption(); | |||||
String message = invalidFields.get(firstErrorField) | |||||
.getLocalizedMessage(); | |||||
/* | |||||
* TODO This should be shown in the editor component once there | |||||
* is a place for that. Optionally, all errors should be shown | |||||
*/ | |||||
Notification.show(caption + ": " + message, Type.ERROR_MESSAGE); | |||||
} else { | } else { | ||||
com.vaadin.server.ErrorEvent.findErrorHandler(Grid.this).error( | com.vaadin.server.ErrorEvent.findErrorHandler(Grid.this).error( | ||||
@Override | @Override | ||||
public void save(int rowIndex) { | public void save(int rowIndex) { | ||||
List<String> errorColumnIds = null; | |||||
boolean success = false; | boolean success = false; | ||||
try { | try { | ||||
saveEditor(); | saveEditor(); | ||||
success = true; | success = true; | ||||
} catch (CommitException e) { | } catch (CommitException e) { | ||||
try { | try { | ||||
getEditorErrorHandler().commitError( | |||||
new CommitErrorEvent(Grid.this, e)); | |||||
CommitErrorEvent event = new CommitErrorEvent( | |||||
Grid.this, e); | |||||
getEditorErrorHandler().commitError(event); | |||||
errorColumnIds = new ArrayList<String>(); | |||||
for (Column column : event.getErrorColumns()) { | |||||
errorColumnIds.add(column.state.id); | |||||
} | |||||
} catch (Exception ee) { | } catch (Exception ee) { | ||||
// A badly written error handler can throw an exception, | // A badly written error handler can throw an exception, | ||||
// which would lock up the Grid | // which would lock up the Grid | ||||
} catch (Exception e) { | } catch (Exception e) { | ||||
handleError(e); | handleError(e); | ||||
} | } | ||||
getEditorRpc().confirmSave(success); | |||||
getEditorRpc().confirmSave(success, errorColumnIds); | |||||
} | } | ||||
private void handleError(Exception e) { | private void handleError(Exception e) { |
*/ | */ | ||||
package com.vaadin.shared.ui.grid; | package com.vaadin.shared.ui.grid; | ||||
import java.util.List; | |||||
import com.vaadin.shared.communication.ClientRpc; | import com.vaadin.shared.communication.ClientRpc; | ||||
/** | /** | ||||
* | * | ||||
* @param saveSucceeded | * @param saveSucceeded | ||||
* <code>true</code> iff the save action was successful | * <code>true</code> iff the save action was successful | ||||
* @param errorColumnsIds | |||||
* a list of column keys that should get error markers, or | |||||
* <code>null</code> if there should be no error markers | |||||
*/ | */ | ||||
void confirmSave(boolean saveSucceeded); | |||||
void confirmSave(boolean saveSucceeded, List<String> errorColumnsIds); | |||||
} | } |
.isElementPresent(By.vaadin("#editor[" + colIndex + "]")); | .isElementPresent(By.vaadin("#editor[" + colIndex + "]")); | ||||
} | } | ||||
/** | |||||
* Checks whether a field is marked with an error. | |||||
* | |||||
* @param colIndex | |||||
* column index | |||||
* @return <code>true</code> iff the field is marked with an error | |||||
*/ | |||||
public boolean isFieldErrorMarked(int colIndex) { | |||||
return getField(colIndex).getAttribute("class").contains("error"); | |||||
} | |||||
/** | /** | ||||
* Saves the fields of this editor. | * Saves the fields of this editor. | ||||
* <p> | * <p> |
.getText()); | .getText()); | ||||
} | } | ||||
@Test | |||||
public void testUneditableColumn() { | public void testUneditableColumn() { | ||||
selectMenuPath("Component", "Editor", "Edit row 5"); | selectMenuPath("Component", "Editor", "Edit row 5"); | ||||
getGridElement().getEditor().isEditable(3)); | getGridElement().getEditor().isEditable(3)); | ||||
} | } | ||||
@Test | |||||
public void testErrorField() { | |||||
selectMenuPath(EDIT_ROW_5); | |||||
assertTrue("No errors should be present", | |||||
getEditor().findElements(By.className("error")).isEmpty()); | |||||
selectMenuPath("Component", "Editor", "Toggle second editor error"); | |||||
getSaveButton().click(); | |||||
assertEquals("Unexpected amount of error fields", 1, getEditor() | |||||
.findElements(By.className("error")).size()); | |||||
} | |||||
protected WebElement getSaveButton() { | protected WebElement getSaveButton() { | ||||
return getEditor().findElement(By.className("v-grid-editor-save")); | return getEditor().findElement(By.className("v-grid-editor-save")); | ||||
} | } |
GridEditorElement editor = getGridElement().getEditor(); | GridEditorElement editor = getGridElement().getEditor(); | ||||
assertFalse( | |||||
"Field 7 should not have been marked with an error before error", | |||||
editor.isFieldErrorMarked(7)); | |||||
WebElement intField = editor.getField(7); | WebElement intField = editor.getField(7); | ||||
intField.clear(); | intField.clear(); | ||||
intField.sendKeys("banana phone"); | intField.sendKeys("banana phone"); | ||||
assertEquals("Column 7: Could not convert value to Integer", | assertEquals("Column 7: Could not convert value to Integer", | ||||
n.getCaption()); | n.getCaption()); | ||||
n.close(); | n.close(); | ||||
assertTrue("Field 7 should have been marked with an error after error", | |||||
editor.isFieldErrorMarked(7)); | |||||
editor.cancel(); | editor.cancel(); | ||||
selectMenuPath(EDIT_ITEM_100); | selectMenuPath(EDIT_ITEM_100); |
package com.vaadin.tests.widgetset.client.grid; | package com.vaadin.tests.widgetset.client.grid; | ||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.Collections; | |||||
import java.util.Date; | import java.util.Date; | ||||
import java.util.HashMap; | import java.util.HashMap; | ||||
import java.util.List; | import java.util.List; | ||||
import com.vaadin.client.widget.grid.events.ScrollHandler; | import com.vaadin.client.widget.grid.events.ScrollHandler; | ||||
import com.vaadin.client.widget.grid.selection.SelectionModel.None; | import com.vaadin.client.widget.grid.selection.SelectionModel.None; | ||||
import com.vaadin.client.widgets.Grid; | import com.vaadin.client.widgets.Grid; | ||||
import com.vaadin.client.widgets.Grid.Column; | |||||
import com.vaadin.client.widgets.Grid.FooterRow; | import com.vaadin.client.widgets.Grid.FooterRow; | ||||
import com.vaadin.client.widgets.Grid.HeaderRow; | import com.vaadin.client.widgets.Grid.HeaderRow; | ||||
import com.vaadin.client.widgets.Grid.SelectionMode; | import com.vaadin.client.widgets.Grid.SelectionMode; | ||||
@Override | @Override | ||||
public void save(EditorRequest<List<Data>> request) { | public void save(EditorRequest<List<Data>> request) { | ||||
if (secondEditorError) { | |||||
log.setText("Syntethic fail of editor in column 2"); | |||||
request.failure(Collections.<Column<?, List<Data>>> singleton(grid | |||||
.getColumn(2))); | |||||
return; | |||||
} | |||||
try { | try { | ||||
log.setText("Row " + request.getRowIndex() + " edit committed"); | log.setText("Row " + request.getRowIndex() + " edit committed"); | ||||
List<Data> rowData = ds.getRow(request.getRowIndex()); | List<Data> rowData = ds.getRow(request.getRowIndex()); | ||||
request.success(); | request.success(); | ||||
} catch (Exception e) { | } catch (Exception e) { | ||||
Logger.getLogger(getClass().getName()).warning(e.toString()); | Logger.getLogger(getClass().getName()).warning(e.toString()); | ||||
request.fail(); | |||||
request.failure(null); | |||||
} | } | ||||
} | } | ||||
private final ListDataSource<List<Data>> ds; | private final ListDataSource<List<Data>> ds; | ||||
private final ListSorter<List<Data>> sorter; | private final ListSorter<List<Data>> sorter; | ||||
private boolean secondEditorError = false; | |||||
/** | /** | ||||
* Our basic data object | * Our basic data object | ||||
*/ | */ | ||||
} | } | ||||
}, "Component", "Editor"); | }, "Component", "Editor"); | ||||
addMenuCommand("Toggle second editor error", new ScheduledCommand() { | |||||
@Override | |||||
public void execute() { | |||||
secondEditorError = !secondEditorError; | |||||
} | |||||
}, "Component", "Editor"); | |||||
} | } | ||||
private void configureFooterRow(final FooterRow row) { | private void configureFooterRow(final FooterRow row) { |