diff --git a/vaadin-grid-flow-parent/vaadin-grid-flow-integration-tests/src/main/java/com/vaadin/flow/component/grid/it/GridMultiSelectionColumnPage.java b/vaadin-grid-flow-parent/vaadin-grid-flow-integration-tests/src/main/java/com/vaadin/flow/component/grid/it/GridMultiSelectionColumnPage.java index 06b70cb95fa..596920537bb 100644 --- a/vaadin-grid-flow-parent/vaadin-grid-flow-integration-tests/src/main/java/com/vaadin/flow/component/grid/it/GridMultiSelectionColumnPage.java +++ b/vaadin-grid-flow-parent/vaadin-grid-flow-integration-tests/src/main/java/com/vaadin/flow/component/grid/it/GridMultiSelectionColumnPage.java @@ -27,7 +27,6 @@ import com.vaadin.flow.component.html.H2; import com.vaadin.flow.component.html.NativeButton; import com.vaadin.flow.data.provider.CallbackDataProvider; -import com.vaadin.flow.data.provider.DataProvider; import com.vaadin.flow.data.provider.Query; import com.vaadin.flow.router.Route; @@ -39,6 +38,10 @@ public class GridMultiSelectionColumnPage extends Div { public static final int ITEM_COUNT = 1000; + static final String IN_MEMORY_GRID_ID = "in-memory-grid"; + static final String DEFINED_ITEM_COUNT_LAZY_GRID_ID = "defined-item-count-lazy-grid"; + static final String UNKNOWN_ITEM_COUNT_LAZY_GRID_ID = "unknown-item-count-lazy-grid"; + private Div message; /** @@ -48,7 +51,8 @@ public GridMultiSelectionColumnPage() { message = new Div(); message.setId("selected-item-count"); - createLazyGrid(); + createDefinedItemCountLazyGrid(); + createUnknownItemCountLazyGrid(); createInMemoryGrid(); createGridWithSwappedDataProvider(); @@ -58,26 +62,34 @@ public GridMultiSelectionColumnPage() { setAutoWidthIsTrueOfSelectionColumn(); } - private void createLazyGrid() { + private void createDefinedItemCountLazyGrid() { Grid lazyGrid = new Grid<>(); - lazyGrid.setItems(DataProvider.fromCallbacks(query -> { - return IntStream - .range(query.getOffset(), - query.getOffset() + query.getLimit()) - .mapToObj(Integer::toString); - }, query -> ITEM_COUNT)); + lazyGrid.setItems(query -> IntStream + .range(query.getOffset(), query.getOffset() + query.getLimit()) + .mapToObj(Integer::toString), query -> ITEM_COUNT); setUp(lazyGrid); - lazyGrid.setId("lazy-grid"); + lazyGrid.setId(DEFINED_ITEM_COUNT_LAZY_GRID_ID); add(new H2("Lazy grid"), lazyGrid); } + private void createUnknownItemCountLazyGrid() { + Grid unknownItemCountLazyGrid = new Grid<>(); + unknownItemCountLazyGrid.setItems(query -> IntStream + .range(query.getOffset(), query.getOffset() + query.getLimit()) + .mapToObj(Integer::toString)); + setUp(unknownItemCountLazyGrid); + unknownItemCountLazyGrid.setId(UNKNOWN_ITEM_COUNT_LAZY_GRID_ID); + + add(new H2("Unknown Item Count Lazy grid"), unknownItemCountLazyGrid); + } + private void createInMemoryGrid() { Grid grid = new Grid<>(); grid.setItems( IntStream.range(0, ITEM_COUNT).mapToObj(Integer::toString)); setUp(grid); - grid.setId("in-memory-grid"); + grid.setId(IN_MEMORY_GRID_ID); add(new H2("In-memory grid"), grid); } diff --git a/vaadin-grid-flow-parent/vaadin-grid-flow-integration-tests/src/test/java/com/vaadin/flow/component/grid/it/GridMultiSelectionColumnPageIT.java b/vaadin-grid-flow-parent/vaadin-grid-flow-integration-tests/src/test/java/com/vaadin/flow/component/grid/it/GridMultiSelectionColumnPageIT.java index 4f96c1a8e4e..e90e6e4131d 100644 --- a/vaadin-grid-flow-parent/vaadin-grid-flow-integration-tests/src/test/java/com/vaadin/flow/component/grid/it/GridMultiSelectionColumnPageIT.java +++ b/vaadin-grid-flow-parent/vaadin-grid-flow-integration-tests/src/test/java/com/vaadin/flow/component/grid/it/GridMultiSelectionColumnPageIT.java @@ -20,24 +20,40 @@ import org.openqa.selenium.By; import org.openqa.selenium.WebElement; -import com.vaadin.tests.AbstractComponentIT; +import com.vaadin.flow.component.grid.testbench.GridElement; import com.vaadin.flow.testutil.TestPath; +import com.vaadin.tests.AbstractComponentIT; @TestPath("vaadin-grid/grid-multi-selection-column") public class GridMultiSelectionColumnPageIT extends AbstractComponentIT { + private static final String SELECT_ALL_CHECKBOX_ID = "selectAllCheckbox"; + @Test public void selectAllCheckbox() { open(); - WebElement lazyGrid = findElement(By.id("lazy-grid")); + WebElement definedItemCountLazyGrid = findElement(By.id( + GridMultiSelectionColumnPage.DEFINED_ITEM_COUNT_LAZY_GRID_ID)); + Assert.assertEquals( + "Defined Item Count Lazy grid selectAllCheckbox should be hidden by default", + "true", + definedItemCountLazyGrid + .findElement(By.id(SELECT_ALL_CHECKBOX_ID)) + .getAttribute("hidden")); + + WebElement unknownItemCountLazyGrid = findElement(By.id( + GridMultiSelectionColumnPage.UNKNOWN_ITEM_COUNT_LAZY_GRID_ID)); Assert.assertEquals( - "lazy grid selectAllCheckbox should be hidden by default", - "true", lazyGrid.findElement(By.id("selectAllCheckbox")) + "Unknown Item Count Lazy grid selectAllCheckbox should be hidden by default", + "true", + unknownItemCountLazyGrid + .findElement(By.id(SELECT_ALL_CHECKBOX_ID)) .getAttribute("hidden")); - WebElement grid = findElement(By.id("in-memory-grid")); + WebElement grid = findElement( + By.id(GridMultiSelectionColumnPage.IN_MEMORY_GRID_ID)); WebElement selectAllCheckbox = grid - .findElement(By.id("selectAllCheckbox")); + .findElement(By.id(SELECT_ALL_CHECKBOX_ID)); Assert.assertNull( "in-memory grid selectAllCheckbox should be visible by default", selectAllCheckbox.getAttribute("hidden")); @@ -73,7 +89,7 @@ public void selectCheckboxMultiSelectionMode() { // Test switch selection mode from single to multi mode before adding the grid to DOM WebElement gridSelectionMode = findElement(By.id("in-testing-multi-selection-mode-grid")); WebElement selectAllCheckbox_selectionMode = gridSelectionMode - .findElement(By.id("selectAllCheckbox")); + .findElement(By.id(SELECT_ALL_CHECKBOX_ID)); WebElement message_selectionMode = findElement(By.id("selected-item-count")); Assert.assertEquals(true, selectAllCheckbox_selectionMode.isDisplayed()); selectAllCheckbox_selectionMode.click(); @@ -96,7 +112,8 @@ public void selectCheckboxMultiSelectionMode() { @Test public void noSelectOnRowItemClick() { open(); - WebElement grid = findElement(By.id("in-memory-grid")); + WebElement grid = findElement( + By.id(GridMultiSelectionColumnPage.IN_MEMORY_GRID_ID)); // click the first row's cell that corresponds to the text column grid.findElements(By.tagName("vaadin-grid-cell-content")).stream() .filter(element -> "0".equals(element.getText())).findFirst() @@ -116,7 +133,7 @@ public void gridWithSwappedDataProvider_selectAllIsNotVisible_swappingDataProvid WebElement grid = findElement(By.id("swapped-grid")); WebElement selectAllCheckbox = grid - .findElement(By.id("selectAllCheckbox")); + .findElement(By.id(SELECT_ALL_CHECKBOX_ID)); Assert.assertEquals("The selectAllCheckbox should be hidden by default", "true", selectAllCheckbox.getAttribute("hidden")); @@ -142,7 +159,7 @@ public void gridWithSwappedDataProvider_selectAllIsForcedVisible_noSelectionEven WebElement grid = findElement(By.id("swapped-grid")); WebElement selectAllCheckbox = grid - .findElement(By.id("selectAllCheckbox")); + .findElement(By.id(SELECT_ALL_CHECKBOX_ID)); executeScript("arguments[0].selectAllHidden = false", selectAllCheckbox); @@ -164,4 +181,36 @@ public void setAutoWidthOfSelectionColumnIsTrue() { String autoWidth = gridSelectionMode.getAttribute("autoWidth"); Assert.assertTrue("autoWidth should be true", Boolean.parseBoolean(autoWidth)); } + + @Test // https://github.com/vaadin/vaadin-flow-components/issues/411 + public void selectRow_gridWithUnknownItemCount_noExceptionThrown() { + open(); + GridElement grid = $(GridElement.class).id( + GridMultiSelectionColumnPage.UNKNOWN_ITEM_COUNT_LAZY_GRID_ID); + + WebElement firstRowSelector = grid + .findElements(By.tagName("vaadin-checkbox")).get(0); + + // Select any row + firstRowSelector.click(); + + // Un-select that row + firstRowSelector.click(); + + // Scroll to a random row outside the initial requested range [0..200] + grid.scrollToRow(199); + grid.scrollToRow(299); + + // Select the first visible row checkbox + WebElement outsideRangeRowSelector = grid + .findElements(By.tagName("vaadin-checkbox")).get(0); + + // Select that row + outsideRangeRowSelector.click(); + // Un-select that row + outsideRangeRowSelector.click(); + + // Check that the count callback not invoked -> no exception thrown + checkLogsForErrors(); + } } diff --git a/vaadin-grid-flow-parent/vaadin-grid-flow/src/main/java/com/vaadin/flow/component/grid/AbstractGridMultiSelectionModel.java b/vaadin-grid-flow-parent/vaadin-grid-flow/src/main/java/com/vaadin/flow/component/grid/AbstractGridMultiSelectionModel.java index b16e625c11e..ad6a8e2dbb9 100644 --- a/vaadin-grid-flow-parent/vaadin-grid-flow/src/main/java/com/vaadin/flow/component/grid/AbstractGridMultiSelectionModel.java +++ b/vaadin-grid-flow-parent/vaadin-grid-flow/src/main/java/com/vaadin/flow/component/grid/AbstractGridMultiSelectionModel.java @@ -32,6 +32,7 @@ import com.vaadin.flow.component.ComponentEventListener; import com.vaadin.flow.component.ComponentUtil; import com.vaadin.flow.component.grid.Grid.AbstractGridExtension; +import com.vaadin.flow.data.provider.DataCommunicator; import com.vaadin.flow.data.provider.DataProvider; import com.vaadin.flow.data.provider.Query; import com.vaadin.flow.data.provider.hierarchy.HierarchicalDataProvider; @@ -115,13 +116,27 @@ public void selectFromClient(T item) { long size = 0; - final DataProvider dataProvider = getGrid() - .getDataCommunicator().getDataProvider(); + if (!isSelectAllCheckboxVisible()) { + // Skip changing the state of Select All checkbox if it was + // meant to be hidden + return; + } + + final DataCommunicator dataCommunicator = getGrid() + .getDataCommunicator(); + + final DataProvider dataProvider = dataCommunicator + .getDataProvider(); // Avoid throwing an IllegalArgumentException in case of // HierarchicalDataProvider if (!(dataProvider instanceof HierarchicalDataProvider)) { - size = dataProvider.size(new Query<>()); + if (dataProvider.isInMemory()) { + size = dataProvider.size(new Query<>()); + } else if (dataCommunicator.isDefinedSize()) { + // Use a cached value of items count for defined size + size = dataCommunicator.getItemCount(); + } } selectionColumn.setSelectAllCheckboxState(size == selected.size()); @@ -300,7 +315,11 @@ public boolean isSelectAllCheckboxVisible() { case HIDDEN: return false; case VISIBLE: - return true; + // Don't show the Select All Checkbox for undefined size, even if + // the visible property is chosen. Select All Checkbox's state + // changing requires a size query, which is not expected for + // undefined size + return getGrid().getDataCommunicator().isDefinedSize(); default: throw new IllegalStateException(String.format( "Select all checkbox visibility is set to an unsupported value: %s", diff --git a/vaadin-grid-flow-parent/vaadin-grid-flow/src/main/java/com/vaadin/flow/component/grid/GridMultiSelectionModel.java b/vaadin-grid-flow-parent/vaadin-grid-flow/src/main/java/com/vaadin/flow/component/grid/GridMultiSelectionModel.java index 6930aa66035..19909f3564e 100644 --- a/vaadin-grid-flow-parent/vaadin-grid-flow/src/main/java/com/vaadin/flow/component/grid/GridMultiSelectionModel.java +++ b/vaadin-grid-flow-parent/vaadin-grid-flow/src/main/java/com/vaadin/flow/component/grid/GridMultiSelectionModel.java @@ -38,33 +38,35 @@ public interface GridMultiSelectionModel * row for the selection column. *

* Default value is {@link #DEFAULT}, which means that the select all is - * only visible if an in-memory data provider is used - * {@link DataProvider#isInMemory()}. + * only visible if an in-memory data is used. */ public enum SelectAllCheckboxVisibility { /** - * Shows the select all checkbox, regardless of data provider used. + * Shows the select all checkbox, if in-memory data is used. *

- * For a lazy data provider, selecting all will result in to all rows - * being fetched from backend to application memory! + * For lazy data, the checkbox is only shown when a count callback has + * been provided. For lazy data with unknown count, the checkbox will + * never be shown. + *

+ * For lazy data, selecting all will result in to all rows being + * fetched from backend to application memory! */ VISIBLE, /** - * Never shows the select all checkbox, regardless of data provider - * used. + * Never shows the select all checkbox, regardless of data is in-memory + * or not (lazy). */ HIDDEN, /** - * By default select all checkbox depends on the grid's dataprovider. + * By default, the visibility of the select all checkbox depends on how + * the Grid's items are fetched: *

    - *
  • Visible, if the data provider is in-memory
  • - *
  • Hidden, if the data provider is NOT in-memory (lazy)
  • + *
  • Visible, if the data is in-memory
  • + *
  • Hidden, if the data is NOT in-memory (lazy)
  • *
- * - * @see DataProvider#isInMemory() */ DEFAULT; } @@ -93,7 +95,12 @@ Registration addMultiSelectionListener( *

* The default value is {@link SelectAllCheckboxVisibility#DEFAULT}, which * means that the checkbox is only visible if the grid's data provider is - * in- memory. + * in-memory. + *

+ * The select all checkbox will never be shown if the Grid uses lazy loading + * with unknown item count, i.e. no items count query provided to it, and + * even setting {@link SelectAllCheckboxVisibility#VISIBLE} won't make it + * visible. * * @param selectAllCheckBoxVisibility * the visiblity mode to use @@ -114,7 +121,12 @@ void setSelectAllCheckboxVisibility( /** * Returns whether the select all checkbox will be visible with the current * setting of - * {@link #setSelectAllCheckboxVisibility(SelectAllCheckboxVisibility)}. + * {@link #setSelectAllCheckboxVisibility(SelectAllCheckboxVisibility)} and + * the type of data set to the Grid (in-memory or lazy). + *

+ * The select all checkbox will never be shown if the Grid uses lazy loading + * with unknown item count, meaning that no count callback has been + * provided. * * @return {@code true} if the checkbox will be visible with the current * settings diff --git a/vaadin-grid-flow-parent/vaadin-grid-flow/src/test/java/com/vaadin/flow/component/grid/AbstractGridMultiSelectionModelTest.java b/vaadin-grid-flow-parent/vaadin-grid-flow/src/test/java/com/vaadin/flow/component/grid/AbstractGridMultiSelectionModelTest.java index 8ce0670b9de..01444a8c8ba 100644 --- a/vaadin-grid-flow-parent/vaadin-grid-flow/src/test/java/com/vaadin/flow/component/grid/AbstractGridMultiSelectionModelTest.java +++ b/vaadin-grid-flow-parent/vaadin-grid-flow/src/test/java/com/vaadin/flow/component/grid/AbstractGridMultiSelectionModelTest.java @@ -20,19 +20,25 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.stream.Stream; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.mockito.Mockito; import com.vaadin.flow.component.grid.Grid.SelectionMode; import com.vaadin.flow.component.treegrid.TreeGrid; +import com.vaadin.flow.data.provider.DataCommunicatorTest; +import com.vaadin.flow.data.provider.DataProvider; +import com.vaadin.flow.data.provider.Query; public class AbstractGridMultiSelectionModelTest { private Set selected; private Set deselected; private Grid grid; + private DataCommunicatorTest.MockUI ui; @Before public void init() { @@ -55,6 +61,8 @@ boolean isInActiveRange(String item) { return true; } }; + ui = new DataCommunicatorTest.MockUI(); + ui.add(grid); } @Test @@ -109,4 +117,224 @@ public void treegrid_select_singleItemSignature_selectFromClient() { Assert.assertEquals(1, grid.getSelectedItems().size()); } + + @Test + public void isSelectAllCheckboxVisible_withInMemoryDataProviderAndDefaultVisibilityMode_visible() { + verifySelectAllCheckboxVisibilityInMultiSelectMode(true, false, true, + GridMultiSelectionModel.SelectAllCheckboxVisibility.DEFAULT); + } + + @Test + public void isSelectAllCheckboxVisible_withDefinedSizeLazyDataProviderAndDefaultVisibilityMode_notVisible() { + verifySelectAllCheckboxVisibilityInMultiSelectMode(false, false, false, + GridMultiSelectionModel.SelectAllCheckboxVisibility.DEFAULT); + } + + @Test + public void isSelectAllCheckboxVisible_withUnknownSizeLazyDataProviderAndDefaultVisibilityMode_notVisible() { + verifySelectAllCheckboxVisibilityInMultiSelectMode(false, true, false, + GridMultiSelectionModel.SelectAllCheckboxVisibility.DEFAULT); + } + + @Test + public void isSelectAllCheckboxVisible_withInMemoryDataProviderAndVisibleMode_visible() { + verifySelectAllCheckboxVisibilityInMultiSelectMode(true, false, true, + GridMultiSelectionModel.SelectAllCheckboxVisibility.VISIBLE); + } + + @Test + public void isSelectAllCheckboxVisible_withDefinedSizeLazyDataProviderAndVisibleMode_visible() { + verifySelectAllCheckboxVisibilityInMultiSelectMode(false, false, true, + GridMultiSelectionModel.SelectAllCheckboxVisibility.VISIBLE); + } + + @Test + public void isSelectAllCheckboxVisible_withUnknownSizeLazyDataProviderAndVisibleMode_notVisible() { + verifySelectAllCheckboxVisibilityInMultiSelectMode(false, true, false, + GridMultiSelectionModel.SelectAllCheckboxVisibility.VISIBLE); + } + + @Test + public void isSelectAllCheckboxVisible_withInMemoryDataProviderAndHiddenMode_notVisible() { + verifySelectAllCheckboxVisibilityInMultiSelectMode(true, false, false, + GridMultiSelectionModel.SelectAllCheckboxVisibility.HIDDEN); + } + + @Test + public void isSelectAllCheckboxVisible_withDefinedSizeLazyDataProviderAndHiddenMode_notVisible() { + verifySelectAllCheckboxVisibilityInMultiSelectMode(false, false, false, + GridMultiSelectionModel.SelectAllCheckboxVisibility.HIDDEN); + } + + @Test + public void isSelectAllCheckboxVisible_withUnknownSizeLazyDataProviderAndHiddenMode_notVisible() { + verifySelectAllCheckboxVisibilityInMultiSelectMode(false, true, false, + GridMultiSelectionModel.SelectAllCheckboxVisibility.HIDDEN); + } + + @Test + public void selectFromClient_inMemoryDataProviderWithDefaultVisibility_updatesCheckboxState() { + verifyUpdateSelectAllCheckboxStateWhenSelectFromClientInMultiSelectMode( + true, false, true, true, + GridMultiSelectionModel.SelectAllCheckboxVisibility.DEFAULT); + } + + @Test + public void selectFromClient_lazyDefinedSizeDataProviderWithDefaultVisibility_skipsUpdateCheckboxState() { + verifyUpdateSelectAllCheckboxStateWhenSelectFromClientInMultiSelectMode( + false, false, false, false, + GridMultiSelectionModel.SelectAllCheckboxVisibility.DEFAULT); + + } + + @Test + public void selectFromClient_lazyUnknownSizeDataProviderWithDefaultVisibility_skipsUpdateCheckboxState() { + verifyUpdateSelectAllCheckboxStateWhenSelectFromClientInMultiSelectMode( + false, true, false, false, + GridMultiSelectionModel.SelectAllCheckboxVisibility.DEFAULT); + + } + + @Test + public void selectFromClient_inMemoryDataProviderWithVisible_updatesCheckboxState() { + verifyUpdateSelectAllCheckboxStateWhenSelectFromClientInMultiSelectMode( + true, false, true, true, + GridMultiSelectionModel.SelectAllCheckboxVisibility.VISIBLE); + } + + @Test + public void selectFromClient_lazyDefinedSizeDataProviderWithVisible_updatesCheckboxState() { + verifyUpdateSelectAllCheckboxStateWhenSelectFromClientInMultiSelectMode( + false, false, false, true, + GridMultiSelectionModel.SelectAllCheckboxVisibility.VISIBLE); + + } + + @Test + public void selectFromClient_lazyUnknownSizeDataProviderWithVisible_skipsUpdateCheckboxState() { + verifyUpdateSelectAllCheckboxStateWhenSelectFromClientInMultiSelectMode( + false, true, false, false, + GridMultiSelectionModel.SelectAllCheckboxVisibility.VISIBLE); + + } + + @Test + public void selectFromClient_inMemoryDataProviderWithHidden_skipsUpdateCheckboxState() { + verifyUpdateSelectAllCheckboxStateWhenSelectFromClientInMultiSelectMode( + true, false, false, false, + GridMultiSelectionModel.SelectAllCheckboxVisibility.HIDDEN); + } + + @Test + public void selectFromClient_lazyDefinedSizeDataProviderWithHidden_skipsUpdateCheckboxState() { + verifyUpdateSelectAllCheckboxStateWhenSelectFromClientInMultiSelectMode( + false, false, false, false, + GridMultiSelectionModel.SelectAllCheckboxVisibility.HIDDEN); + + } + + @Test + public void selectFromClient_lazyUnknownSizeDataProviderWithHidden_skipsUpdateCheckboxState() { + verifyUpdateSelectAllCheckboxStateWhenSelectFromClientInMultiSelectMode( + false, true, false, false, + GridMultiSelectionModel.SelectAllCheckboxVisibility.HIDDEN); + + } + + private void verifySelectAllCheckboxVisibilityInMultiSelectMode( + boolean inMemory, boolean unknownItemCount, + boolean expectedVisibility, + GridMultiSelectionModel.SelectAllCheckboxVisibility visibility) { + customiseMultiSelectGridAndDataProvider(inMemory, unknownItemCount, + visibility, false); + + boolean selectAllCheckboxVisible = ((GridMultiSelectionModel) grid + .getSelectionModel()).isSelectAllCheckboxVisible(); + + Assert.assertEquals( + "Unexpected select all checkbox visibility in multi-select mode", + expectedVisibility, selectAllCheckboxVisible); + } + + private void verifyUpdateSelectAllCheckboxStateWhenSelectFromClientInMultiSelectMode( + boolean inMemory, boolean unknownItemCount, + boolean expectedSizeQuery, boolean expectedCheckboxStateUpdate, + GridMultiSelectionModel.SelectAllCheckboxVisibility visibility) { + DataProvider dataProvider = customiseMultiSelectGridAndDataProvider( + inMemory, unknownItemCount, visibility, true); + + fakeClientCommunication(); + + Mockito.reset(dataProvider); + + Mockito.when(dataProvider.isInMemory()).thenReturn(inMemory); + + grid.getSelectionModel().selectFromClient("foo"); + + Mockito.verify(dataProvider, Mockito.times(expectedSizeQuery ? 1 : 0)) + .size(Mockito.any(Query.class)); + + grid.getSelectionModel().selectFromClient("bar"); + + Mockito.verify(dataProvider, Mockito.times(expectedSizeQuery ? 2 : 0)) + .size(Mockito.any(Query.class)); + + Assert.assertEquals(expectedCheckboxStateUpdate ? "true" : "false", + grid.getElement().getChild(0).getProperty("selectAll")); + } + + private DataProvider customiseMultiSelectGridAndDataProvider( + boolean inMemory, boolean unknownItemCount, + GridMultiSelectionModel.SelectAllCheckboxVisibility visibility, + boolean mockDataProvider) { + grid.setSelectionMode(SelectionMode.MULTI); + + GridMultiSelectionModel selectionModel = (GridMultiSelectionModel) grid + .getSelectionModel(); + selectionModel.setSelectAllCheckboxVisibility(visibility); + + DataProvider dataProvider; + + if (inMemory) { + dataProvider = getInMemoryDataProvider(); + } else if (unknownItemCount) { + dataProvider = getUnknownItemCountLazyDataProvider(); + } else { + dataProvider = getDefinedSizeLazyDataProvider(); + } + + if (mockDataProvider) { + dataProvider = Mockito.spy(dataProvider); + } + + grid.setDataProvider(dataProvider); + grid.getDataCommunicator().setDefinedSize(!unknownItemCount); + return dataProvider; + } + + private DataProvider getDefinedSizeLazyDataProvider() { + return DataProvider.fromCallbacks( + query -> Stream.of("foo", "bar").skip(query.getOffset()) + .limit(query.getLimit()), + query -> (int) Stream.of("foo", "bar").skip(query.getOffset()) + .limit(query.getLimit()).count()); + } + + private DataProvider getUnknownItemCountLazyDataProvider() { + return DataProvider.fromCallbacks(query -> Stream.of("foo", "bar") + .skip(query.getOffset()).limit(query.getLimit()), query -> { + Assert.fail("Unexpected size query call"); + return 0; + }); + } + + private DataProvider getInMemoryDataProvider() { + return DataProvider.ofItems("foo", "bar"); + } + + private void fakeClientCommunication() { + ui.getInternals().getStateTree().runExecutionsBeforeClientResponse(); + ui.getInternals().getStateTree().collectChanges(ignore -> { + }); + } }