Change-Id: Ieb245205b3a311a4563f39bc48baadc44e218b61tags/7.4.0.rc1
@@ -57,6 +57,7 @@ import com.vaadin.client.widget.grid.events.SelectAllHandler; | |||
import com.vaadin.client.widget.grid.selection.AbstractRowHandleSelectionModel; | |||
import com.vaadin.client.widget.grid.selection.SelectionEvent; | |||
import com.vaadin.client.widget.grid.selection.SelectionHandler; | |||
import com.vaadin.client.widget.grid.selection.SelectionModel; | |||
import com.vaadin.client.widget.grid.selection.SelectionModelMulti; | |||
import com.vaadin.client.widget.grid.selection.SelectionModelNone; | |||
import com.vaadin.client.widget.grid.selection.SelectionModelSingle; | |||
@@ -536,7 +537,12 @@ public class GridConnector extends AbstractHasComponentsConnector implements | |||
// Selection | |||
if (stateChangeEvent.hasPropertyChanged("selectionMode")) { | |||
onSelectionModeChange(); | |||
updateSelectDeselectAllowed(); | |||
} else if (stateChangeEvent | |||
.hasPropertyChanged("singleSelectDeselectAllowed")) { | |||
updateSelectDeselectAllowed(); | |||
} | |||
if (stateChangeEvent.hasPropertyChanged("selectedKeys")) { | |||
updateSelectionFromState(); | |||
} | |||
@@ -567,6 +573,14 @@ public class GridConnector extends AbstractHasComponentsConnector implements | |||
} | |||
} | |||
private void updateSelectDeselectAllowed() { | |||
SelectionModel<JsonObject> model = getWidget().getSelectionModel(); | |||
if (model instanceof SelectionModel.Single<?>) { | |||
((SelectionModel.Single<?>) model) | |||
.setDeselectAllowed(getState().singleSelectDeselectAllowed); | |||
} | |||
} | |||
private void updateColumnOrderFromState(List<String> stateColumnOrder) { | |||
CustomGridColumn[] columns = new CustomGridColumn[stateColumnOrder | |||
.size()]; |
@@ -30,6 +30,7 @@ public class ClickSelectHandler<T> { | |||
private Grid<T> grid; | |||
private HandlerRegistration clickHandler; | |||
private boolean deselectAllowed = true; | |||
private class RowClickHandler implements BodyClickHandler { | |||
@@ -38,6 +39,8 @@ public class ClickSelectHandler<T> { | |||
T row = (T) event.getTargetCell().getRow(); | |||
if (!grid.isSelected(row)) { | |||
grid.select(row); | |||
} else if (deselectAllowed) { | |||
grid.deselect(row); | |||
} | |||
} | |||
} | |||
@@ -60,4 +63,15 @@ public class ClickSelectHandler<T> { | |||
public void removeHandler() { | |||
clickHandler.removeHandler(); | |||
} | |||
/** | |||
* Sets whether clicking the currently selected row should deselect the row. | |||
* | |||
* @param deselectAllowed | |||
* <code>true</code> to allow deselecting the selected row; | |||
* otherwise <code>false</code> | |||
*/ | |||
public void setDeselectAllowed(boolean deselectAllowed) { | |||
this.deselectAllowed = deselectAllowed; | |||
} | |||
} |
@@ -116,6 +116,26 @@ public interface SelectionModel<T> { | |||
*/ | |||
public T getSelectedRow(); | |||
/** | |||
* Sets whether it's allowed to deselect the selected row through the | |||
* UI. Deselection is allowed by default. | |||
* | |||
* @param deselectAllowed | |||
* <code>true</code> if the selected row can be deselected | |||
* without selecting another row instead; otherwise | |||
* <code>false</code>. | |||
*/ | |||
public void setDeselectAllowed(boolean deselectAllowed); | |||
/** | |||
* Sets whether it's allowed to deselect the selected row through the | |||
* UI. | |||
* | |||
* @return <code>true</code> if deselection is allowed; otherwise | |||
* <code>false</code> | |||
*/ | |||
public boolean isDeselectAllowed(); | |||
} | |||
/** |
@@ -40,6 +40,8 @@ public class SelectionModelSingle<T> extends AbstractRowHandleSelectionModel<T> | |||
/** Event handling for selection by clicking cells */ | |||
private ClickSelectHandler<T> clickSelectHandler; | |||
private boolean deselectAllowed = true; | |||
@Override | |||
public boolean isSelected(T row) { | |||
return selectedRow != null | |||
@@ -66,6 +68,7 @@ public class SelectionModelSingle<T> extends AbstractRowHandleSelectionModel<T> | |||
if (this.grid != null) { | |||
spaceSelectHandler = new SpaceSelectHandler<T>(grid); | |||
clickSelectHandler = new ClickSelectHandler<T>(grid); | |||
updateHandlerDeselectAllowed(); | |||
} else { | |||
spaceSelectHandler.removeHandler(); | |||
clickSelectHandler.removeHandler(); | |||
@@ -148,4 +151,25 @@ public class SelectionModelSingle<T> extends AbstractRowHandleSelectionModel<T> | |||
return false; | |||
} | |||
} | |||
@Override | |||
public void setDeselectAllowed(boolean deselectAllowed) { | |||
this.deselectAllowed = deselectAllowed; | |||
updateHandlerDeselectAllowed(); | |||
} | |||
@Override | |||
public boolean isDeselectAllowed() { | |||
return deselectAllowed; | |||
} | |||
private void updateHandlerDeselectAllowed() { | |||
if (spaceSelectHandler != null) { | |||
spaceSelectHandler.setDeselectAllowed(deselectAllowed); | |||
} | |||
if (clickSelectHandler != null) { | |||
clickSelectHandler.setDeselectAllowed(deselectAllowed); | |||
} | |||
} | |||
} |
@@ -79,10 +79,10 @@ public class SpaceSelectHandler<T> { | |||
protected void setSelected(Grid<T> grid, int rowIndex) { | |||
T row = grid.getDataSource().getRow(rowIndex); | |||
if (grid.isSelected(row)) { | |||
grid.deselect(row); | |||
} else { | |||
if (!grid.isSelected(row)) { | |||
grid.select(row); | |||
} else if (deselectAllowed) { | |||
grid.deselect(row); | |||
} | |||
} | |||
} | |||
@@ -91,6 +91,7 @@ public class SpaceSelectHandler<T> { | |||
private Grid<T> grid; | |||
private HandlerRegistration spaceUpHandler; | |||
private HandlerRegistration spaceDownHandler; | |||
private boolean deselectAllowed = true; | |||
/** | |||
* Constructor for SpaceSelectHandler. This constructor will add all | |||
@@ -121,4 +122,16 @@ public class SpaceSelectHandler<T> { | |||
spaceDownHandler.removeHandler(); | |||
spaceUpHandler.removeHandler(); | |||
} | |||
/** | |||
* Sets whether pressing space for the currently selected row should | |||
* deselect the row. | |||
* | |||
* @param deselectAllowed | |||
* <code>true</code> to allow deselecting the selected row; | |||
* otherwise <code>false</code> | |||
*/ | |||
public void setDeselectAllowed(boolean deselectAllowed) { | |||
this.deselectAllowed = deselectAllowed; | |||
} | |||
} |
@@ -642,6 +642,26 @@ public class Grid extends AbstractComponent implements SelectionNotifier, | |||
* <code>null</code> if nothing is selected | |||
*/ | |||
Object getSelectedRow(); | |||
/** | |||
* Sets whether it's allowed to deselect the selected row through | |||
* the UI. Deselection is allowed by default. | |||
* | |||
* @param deselectAllowed | |||
* <code>true</code> if the selected row can be | |||
* deselected without selecting another row instead; | |||
* otherwise <code>false</code>. | |||
*/ | |||
public void setDeselectAllowed(boolean deselectAllowed); | |||
/** | |||
* Sets whether it's allowed to deselect the selected row through | |||
* the UI. | |||
* | |||
* @return <code>true</code> if deselection is allowed; otherwise | |||
* <code>false</code> | |||
*/ | |||
public boolean isDeselectAllowed(); | |||
} | |||
/** | |||
@@ -815,6 +835,16 @@ public class Grid extends AbstractComponent implements SelectionNotifier, | |||
public void reset() { | |||
deselect(getSelectedRow()); | |||
} | |||
@Override | |||
public void setDeselectAllowed(boolean deselectAllowed) { | |||
grid.getState().singleSelectDeselectAllowed = deselectAllowed; | |||
} | |||
@Override | |||
public boolean isDeselectAllowed() { | |||
return grid.getState(false).singleSelectDeselectAllowed; | |||
} | |||
} | |||
/** |
@@ -132,6 +132,9 @@ public class GridState extends AbstractComponentState { | |||
public SharedSelectionMode selectionMode; | |||
/** Whether single select mode can be cleared through the UI */ | |||
public boolean singleSelectDeselectAllowed = true; | |||
/** Keys of the currently sorted columns */ | |||
public String[] sortColumns = new String[0]; | |||
@@ -57,6 +57,7 @@ import com.vaadin.ui.Grid.MultiSelectionModel; | |||
import com.vaadin.ui.Grid.RowReference; | |||
import com.vaadin.ui.Grid.RowStyleGenerator; | |||
import com.vaadin.ui.Grid.SelectionMode; | |||
import com.vaadin.ui.Grid.SelectionModel; | |||
import com.vaadin.ui.renderer.DateRenderer; | |||
import com.vaadin.ui.renderer.HtmlRenderer; | |||
import com.vaadin.ui.renderer.NumberRenderer; | |||
@@ -82,6 +83,8 @@ public class GridBasicFeatures extends AbstractComponentTest<Grid> { | |||
private int containerDelay = 0; | |||
private boolean singleSelectAllowDeselect = true; | |||
private IndexedContainer ds; | |||
private Grid grid; | |||
private SelectionListener selectionListener = new SelectionListener() { | |||
@@ -320,6 +323,9 @@ public class GridBasicFeatures extends AbstractComponentTest<Grid> { | |||
grid.setSelectionMode(selectionMode); | |||
if (selectionMode == SelectionMode.SINGLE) { | |||
grid.addSelectionListener(selectionListener); | |||
((SelectionModel.Single) grid.getSelectionModel()) | |||
.setDeselectAllowed(singleSelectAllowDeselect); | |||
} else { | |||
grid.removeSelectionListener(selectionListener); | |||
} | |||
@@ -488,6 +494,20 @@ public class GridBasicFeatures extends AbstractComponentTest<Grid> { | |||
} | |||
}); | |||
createBooleanAction("Single select allow deselect", "State", | |||
singleSelectAllowDeselect, new Command<Grid, Boolean>() { | |||
@Override | |||
public void execute(Grid c, Boolean value, Object data) { | |||
singleSelectAllowDeselect = value.booleanValue(); | |||
SelectionModel model = c.getSelectionModel(); | |||
if (model instanceof SelectionModel.Single) { | |||
((SelectionModel.Single) model) | |||
.setDeselectAllowed(singleSelectAllowDeselect); | |||
} | |||
} | |||
}); | |||
} | |||
protected void createHeaderActions() { |
@@ -19,6 +19,8 @@ import static org.junit.Assert.assertFalse; | |||
import static org.junit.Assert.assertTrue; | |||
import org.junit.Test; | |||
import org.openqa.selenium.Keys; | |||
import org.openqa.selenium.interactions.Actions; | |||
import com.vaadin.testbench.By; | |||
import com.vaadin.testbench.elements.GridElement.GridCellElement; | |||
@@ -74,7 +76,7 @@ public class GridClientSelectionTest extends GridBasicClientFeaturesTest { | |||
assertTrue("Multi Selection Model should have select all checkbox", | |||
header.isElementPresent(By.tagName("input"))); | |||
setSelectionModelSingle(); | |||
setSelectionModelSingle(true); | |||
header = getGridElement().getHeaderCell(0, 0); | |||
assertFalse( | |||
"Check box shouldn't have been in header for Single Selection Model", | |||
@@ -88,15 +90,133 @@ public class GridClientSelectionTest extends GridBasicClientFeaturesTest { | |||
} | |||
@Test | |||
public void testDeselectAllowedMouseInput() { | |||
openTestURL(); | |||
setSelectionModelSingle(true); | |||
getGridElement().getCell(5, 1).click(); | |||
assertTrue("Row 5 should be selected after clicking", isRowSelected(5)); | |||
getGridElement().getCell(7, 1).click(); | |||
assertFalse("Row 5 should be deselected after clicking another row", | |||
isRowSelected(5)); | |||
assertTrue("Row 7 should be selected after clicking", isRowSelected(7)); | |||
getGridElement().getCell(7, 1).click(); | |||
assertFalse("Row should be deselected after clicking again", | |||
isRowSelected(7)); | |||
} | |||
@Test | |||
public void testDeselectAllowedKeyboardInput() { | |||
openTestURL(); | |||
setSelectionModelSingle(true); | |||
getGridElement().getHeaderCell(0, 1).click(); | |||
new Actions(getDriver()).sendKeys(Keys.ARROW_DOWN).perform(); | |||
new Actions(getDriver()).sendKeys(Keys.SPACE).perform(); | |||
assertTrue("Row 0 should be selected after pressing space", | |||
isRowSelected(0)); | |||
new Actions(getDriver()).sendKeys(Keys.ARROW_DOWN).perform(); | |||
new Actions(getDriver()).sendKeys(Keys.SPACE).perform(); | |||
assertFalse( | |||
"Row 0 should be deselected after pressing space another row", | |||
isRowSelected(0)); | |||
assertTrue("Row 1 should be selected after pressing space", | |||
isRowSelected(1)); | |||
new Actions(getDriver()).sendKeys(Keys.SPACE).perform(); | |||
assertFalse("Row should be deselected after pressing space again", | |||
isRowSelected(1)); | |||
} | |||
@Test | |||
public void testDeselectNotAllowedMouseInput() { | |||
openTestURL(); | |||
setSelectionModelSingle(false); | |||
getGridElement().getCell(5, 1).click(); | |||
assertTrue("Row 5 should be selected after clicking", isRowSelected(5)); | |||
getGridElement().getCell(7, 1).click(); | |||
assertFalse("Row 5 should be deselected after clicking another row", | |||
isRowSelected(5)); | |||
assertTrue("Row 7 should be selected after clicking", isRowSelected(7)); | |||
getGridElement().getCell(7, 1).click(); | |||
assertTrue("Row should remain selected after clicking again", | |||
isRowSelected(7)); | |||
} | |||
@Test | |||
public void testDeselectNotAllowedKeyboardInput() { | |||
openTestURL(); | |||
setSelectionModelSingle(false); | |||
getGridElement().getHeaderCell(0, 1).click(); | |||
new Actions(getDriver()).sendKeys(Keys.ARROW_DOWN).perform(); | |||
new Actions(getDriver()).sendKeys(Keys.SPACE).perform(); | |||
assertTrue("Row 0 should be selected after pressing space", | |||
isRowSelected(0)); | |||
new Actions(getDriver()).sendKeys(Keys.ARROW_DOWN).perform(); | |||
new Actions(getDriver()).sendKeys(Keys.SPACE).perform(); | |||
assertFalse( | |||
"Row 0 should be deselected after pressing space another row", | |||
isRowSelected(0)); | |||
assertTrue("Row 1 should be selected after pressing space", | |||
isRowSelected(1)); | |||
new Actions(getDriver()).sendKeys(Keys.SPACE).perform(); | |||
assertTrue("Row should remain selected after pressing space again", | |||
isRowSelected(1)); | |||
} | |||
private boolean isRowSelected(int index) { | |||
boolean selected = getGridElement().getRow(index).isSelected(); | |||
return selected; | |||
} | |||
private void setSelectionModelMulti() { | |||
selectMenuPath("Component", "State", "Selection mode", "multi"); | |||
setSelectionModel("multi"); | |||
} | |||
private void setSelectionModelSingle() { | |||
selectMenuPath("Component", "State", "Selection mode", "single"); | |||
private void setSelectionModelSingle(boolean deselectAllowed) { | |||
String mode = "single"; | |||
if (!deselectAllowed) { | |||
mode += " (no deselect)"; | |||
} | |||
setSelectionModel(mode); | |||
} | |||
private void setSelectionModelNone() { | |||
selectMenuPath("Component", "State", "Selection mode", "none"); | |||
setSelectionModel("none"); | |||
} | |||
private void setSelectionModel(String model) { | |||
selectMenuPath("Component", "State", "Selection mode", model); | |||
} | |||
} |
@@ -270,6 +270,41 @@ public class GridSelectionTest extends GridBasicFeaturesTest { | |||
} | |||
@Test | |||
public void testToggleDeselectAllowed() { | |||
openTestURL(); | |||
setSelectionModelSingle(); | |||
// Deselect allowed already enabled | |||
getGridElement().getCell(5, 1).click(); | |||
getGridElement().getCell(5, 1).click(); | |||
assertFalse("Row should be not selected after two clicks", getRow(5) | |||
.isSelected()); | |||
selectMenuPath("Component", "State", "Single select allow deselect"); | |||
getGridElement().getCell(5, 1).click(); | |||
getGridElement().getCell(5, 1).click(); | |||
assertTrue("Row should be selected after two clicks", getRow(5) | |||
.isSelected()); | |||
selectMenuPath("Component", "State", "Single select allow deselect"); | |||
getGridElement().getCell(5, 1).click(); | |||
assertFalse("Row should be not selected after another click", getRow(5) | |||
.isSelected()); | |||
// Also verify that state is updated together with the model | |||
setSelectionModelNone(); | |||
selectMenuPath("Component", "State", "Single select allow deselect"); | |||
setSelectionModelSingle(); | |||
getGridElement().getCell(5, 1).click(); | |||
getGridElement().getCell(5, 1).click(); | |||
assertTrue("Row should stay selected after two clicks", getRow(5) | |||
.isSelected()); | |||
} | |||
private void setSelectionModelMulti() { | |||
selectMenuPath("Component", "State", "Selection mode", "multi"); | |||
} |
@@ -66,6 +66,7 @@ import com.vaadin.client.widget.grid.events.HeaderKeyPressHandler; | |||
import com.vaadin.client.widget.grid.events.HeaderKeyUpHandler; | |||
import com.vaadin.client.widget.grid.events.ScrollEvent; | |||
import com.vaadin.client.widget.grid.events.ScrollHandler; | |||
import com.vaadin.client.widget.grid.selection.SelectionModel; | |||
import com.vaadin.client.widget.grid.selection.SelectionModel.None; | |||
import com.vaadin.client.widgets.Grid; | |||
import com.vaadin.client.widgets.Grid.Column; | |||
@@ -468,6 +469,15 @@ public class GridBasicClientFeaturesWidget extends | |||
} | |||
}, selectionModePath); | |||
addMenuCommand("single (no deselect)", new ScheduledCommand() { | |||
@Override | |||
public void execute() { | |||
grid.setSelectionMode(SelectionMode.SINGLE); | |||
((SelectionModel.Single<?>) grid.getSelectionModel()) | |||
.setDeselectAllowed(false); | |||
} | |||
}, selectionModePath); | |||
addMenuCommand("none", new ScheduledCommand() { | |||
@Override | |||
public void execute() { |