Browse Source

Add simple data change handling for Selects

Change-Id: I16f9577ea4091fb4febe167d76e141b5945f53ab
tags/8.0.0.alpha1
Teemu Suo-Anttila 7 years ago
parent
commit
6d0204cafe
24 changed files with 364 additions and 19 deletions
  1. 1
    1
      client/src/main/java/com/vaadin/client/data/AbstractRemoteDataSource.java
  2. 1
    1
      client/src/main/java/com/vaadin/client/data/CacheStrategy.java
  3. 21
    1
      client/src/main/java/com/vaadin/client/data/DataSource.java
  4. 103
    0
      client/src/main/java/com/vaadin/client/data/SimpleDataChangeHandler.java
  5. 1
    1
      client/src/main/java/com/vaadin/client/widget/escalator/RowVisibilityChangeEvent.java
  6. 1
    1
      client/src/main/java/com/vaadin/client/widget/grid/DataAvailableEvent.java
  7. 1
    1
      client/src/main/java/com/vaadin/client/widgets/Escalator.java
  8. 1
    1
      client/src/main/java/com/vaadin/client/widgets/Grid.java
  9. 1
    1
      client/src/test/java/com/vaadin/client/ui/grid/PartitioningTest.java
  10. 1
    1
      compatibility-client/src/main/java/com/vaadin/v7/client/connectors/MultiSelectionModelConnector.java
  11. 1
    1
      compatibility-client/src/main/java/com/vaadin/v7/client/connectors/RpcDataSourceConnector.java
  12. 1
    1
      compatibility-client/src/main/java/com/vaadin/v7/client/widget/escalator/RowVisibilityChangeEvent.java
  13. 1
    1
      compatibility-client/src/main/java/com/vaadin/v7/client/widget/grid/DataAvailableEvent.java
  14. 1
    1
      compatibility-client/src/main/java/com/vaadin/v7/client/widgets/Escalator.java
  15. 1
    1
      compatibility-client/src/main/java/com/vaadin/v7/client/widgets/Grid.java
  16. 1
    1
      compatibility-server/src/main/java/com/vaadin/v7/server/communication/data/RpcDataProviderExtension.java
  17. 1
    1
      server/src/main/java/com/vaadin/server/data/DataCommunicator.java
  18. 1
    1
      shared/src/main/java/com/vaadin/shared/Range.java
  19. 2
    0
      shared/src/test/java/com/vaadin/shared/ui/grid/RangeTest.java
  20. 117
    0
      uitest/src/main/java/com/vaadin/tests/data/DummyData.java
  21. 43
    0
      uitest/src/main/java/com/vaadin/tests/widgetset/client/data/DummyComponentConnector.java
  22. 1
    1
      uitest/src/test/java/com/vaadin/tests/components/grid/basicfeatures/client/GridDetailsClientTest.java
  23. 1
    1
      uitest/src/test/java/com/vaadin/tests/components/grid/basicfeatures/escalator/EscalatorSpacerTest.java
  24. 60
    0
      uitest/src/test/java/com/vaadin/tests/data/DummyDataTest.java

+ 1
- 1
client/src/main/java/com/vaadin/client/data/AbstractRemoteDataSource.java View File

@@ -28,8 +28,8 @@ import com.google.gwt.core.client.Duration;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.vaadin.client.Profiler;
import com.vaadin.shared.Range;
import com.vaadin.shared.Registration;
import com.vaadin.shared.ui.grid.Range;

/**
* Base implementation for data sources that fetch data from a remote system.

+ 1
- 1
client/src/main/java/com/vaadin/client/data/CacheStrategy.java View File

@@ -16,7 +16,7 @@

package com.vaadin.client.data;

import com.vaadin.shared.ui.grid.Range;
import com.vaadin.shared.Range;

/**
* Determines what data an {@link AbstractRemoteDataSource} should fetch and

+ 21
- 1
client/src/main/java/com/vaadin/client/data/DataSource.java View File

@@ -16,6 +16,9 @@

package com.vaadin.client.data;

import java.util.function.Consumer;

import com.vaadin.shared.Range;
import com.vaadin.shared.Registration;

/**
@@ -104,7 +107,7 @@ public interface DataSource<T> {
* override of an existing method, we're defining a new method for that
* instead.
*
* @param rowHandle
* @param obj
* the reference object with which to compare
* @return {@code true} if this object is the same as the obj argument;
* {@code false} otherwise.
@@ -182,10 +185,27 @@ public interface DataSource<T> {
*
* @param dataChangeHandler
* the data change handler
*
* @return registration for removing the handler
*/
public Registration addDataChangeHandler(
DataChangeHandler dataChangeHandler);

/**
* Sets a simple data change handler for a widget without lazy loading.
* Refresh method should reset all the data in the widget.
*
* @param refreshMethod
* a method to refresh all data in the widget
*
* @return registration for removing the handler
*/
public default Registration addDataChangeHandler(
Consumer<Range> refreshMethod) {
return addDataChangeHandler(
new SimpleDataChangeHandler(this, refreshMethod));
}

/**
* Gets a {@link RowHandle} of a row object in the cache.
*

+ 103
- 0
client/src/main/java/com/vaadin/client/data/SimpleDataChangeHandler.java View File

@@ -0,0 +1,103 @@
/*
* Copyright 2000-2016 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.vaadin.client.data;

import java.util.function.Consumer;

import com.google.gwt.core.client.Scheduler;
import com.vaadin.shared.Range;

/**
* Helper class for creating a {@link DataChangeHandler} for a Widget that does
* not support lazy loading.
*
* @author Vaadin Ltd
* @since
*/
public class SimpleDataChangeHandler implements DataChangeHandler {

/**
* Class to request the data source to get the full data set.
*/
private static class DelayedResetScheduler {

private final DataSource<?> dataSource;
private boolean scheduled = false;

public DelayedResetScheduler(DataSource<?> dataSource) {
this.dataSource = dataSource;
}

public void schedule() {
if (scheduled) {
return;
}
Scheduler.get().scheduleFinally(() -> {
dataSource.ensureAvailability(0, dataSource.size());
scheduled = false;
});
scheduled = true;
}

public int getExpectedSize() {
return dataSource.size();
}

public boolean isScheduled() {
return scheduled;
}
}

private final DelayedResetScheduler scheduler;
private final Consumer<Range> refreshMethod;

SimpleDataChangeHandler(DataSource<?> dataSource,
Consumer<Range> refreshMethod) {
scheduler = new DelayedResetScheduler(dataSource);
this.refreshMethod = refreshMethod;
}

@Override
public void dataUpdated(int firstRowIndex, int numberOfRows) {
scheduler.schedule();
}

@Override
public void dataRemoved(int firstRowIndex, int numberOfRows) {
scheduler.schedule();
}

@Override
public void dataAdded(int firstRowIndex, int numberOfRows) {
scheduler.schedule();
}

@Override
public void dataAvailable(int firstRowIndex, int numberOfRows) {
if (!scheduler.isScheduled() && firstRowIndex == 0
&& numberOfRows == scheduler.getExpectedSize()) {
// All data should now be available.
refreshMethod.accept(Range.withLength(firstRowIndex, numberOfRows));
} else {
scheduler.schedule();
}
}

@Override
public void resetDataAndSize(int newSize) {
scheduler.schedule();
}
}

+ 1
- 1
client/src/main/java/com/vaadin/client/widget/escalator/RowVisibilityChangeEvent.java View File

@@ -17,7 +17,7 @@
package com.vaadin.client.widget.escalator;

import com.google.gwt.event.shared.GwtEvent;
import com.vaadin.shared.ui.grid.Range;
import com.vaadin.shared.Range;

/**
* Event fired when the range of visible rows changes e.g. because of scrolling.

+ 1
- 1
client/src/main/java/com/vaadin/client/widget/grid/DataAvailableEvent.java View File

@@ -16,7 +16,7 @@
package com.vaadin.client.widget.grid;

import com.google.gwt.event.shared.GwtEvent;
import com.vaadin.shared.ui.grid.Range;
import com.vaadin.shared.Range;

/**
* Event object describing a change of row availability in DataSource of a Grid.

+ 1
- 1
client/src/main/java/com/vaadin/client/widgets/Escalator.java View File

@@ -89,8 +89,8 @@ import com.vaadin.client.widget.escalator.events.RowHeightChangedEvent;
import com.vaadin.client.widget.grid.events.ScrollEvent;
import com.vaadin.client.widget.grid.events.ScrollHandler;
import com.vaadin.client.widgets.Escalator.JsniUtil.TouchHandlerBundle;
import com.vaadin.shared.Range;
import com.vaadin.shared.ui.grid.HeightMode;
import com.vaadin.shared.ui.grid.Range;
import com.vaadin.shared.ui.grid.ScrollDestination;
import com.vaadin.shared.util.SharedUtil;


+ 1
- 1
client/src/main/java/com/vaadin/client/widgets/Grid.java View File

@@ -174,13 +174,13 @@ import com.vaadin.client.widgets.Escalator.SubPartArguments;
import com.vaadin.client.widgets.Grid.Editor.State;
import com.vaadin.client.widgets.Grid.StaticSection.StaticCell;
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.GridConstants;
import com.vaadin.shared.ui.grid.GridConstants.Section;
import com.vaadin.shared.ui.grid.GridStaticCellType;
import com.vaadin.shared.ui.grid.HeightMode;
import com.vaadin.shared.ui.grid.Range;
import com.vaadin.shared.ui.grid.ScrollDestination;
import com.vaadin.shared.util.SharedUtil;


+ 1
- 1
client/src/test/java/com/vaadin/client/ui/grid/PartitioningTest.java View File

@@ -21,7 +21,7 @@ import static org.junit.Assert.assertTrue;

import org.junit.Test;

import com.vaadin.shared.ui.grid.Range;
import com.vaadin.shared.Range;

@SuppressWarnings("static-method")
public class PartitioningTest {

+ 1
- 1
compatibility-client/src/main/java/com/vaadin/v7/client/connectors/MultiSelectionModelConnector.java View File

@@ -29,8 +29,8 @@ import com.vaadin.client.ServerConnector;
import com.vaadin.client.annotations.OnStateChange;
import com.vaadin.client.data.DataSource;
import com.vaadin.client.data.DataSource.RowHandle;
import com.vaadin.shared.Range;
import com.vaadin.shared.ui.Connect;
import com.vaadin.shared.ui.grid.Range;
import com.vaadin.v7.client.renderers.ComplexRenderer;
import com.vaadin.v7.client.renderers.Renderer;
import com.vaadin.v7.client.widget.grid.DataAvailableEvent;

+ 1
- 1
compatibility-client/src/main/java/com/vaadin/v7/client/connectors/RpcDataSourceConnector.java View File

@@ -23,10 +23,10 @@ import java.util.List;
import com.vaadin.client.ServerConnector;
import com.vaadin.client.data.AbstractRemoteDataSource;
import com.vaadin.client.extensions.AbstractExtensionConnector;
import com.vaadin.shared.Range;
import com.vaadin.shared.data.DataProviderRpc;
import com.vaadin.shared.data.DataRequestRpc;
import com.vaadin.shared.ui.Connect;
import com.vaadin.shared.ui.grid.Range;
import com.vaadin.v7.shared.ui.grid.GridState;

import elemental.json.Json;

+ 1
- 1
compatibility-client/src/main/java/com/vaadin/v7/client/widget/escalator/RowVisibilityChangeEvent.java View File

@@ -17,7 +17,7 @@
package com.vaadin.v7.client.widget.escalator;

import com.google.gwt.event.shared.GwtEvent;
import com.vaadin.shared.ui.grid.Range;
import com.vaadin.shared.Range;

/**
* Event fired when the range of visible rows changes e.g. because of scrolling.

+ 1
- 1
compatibility-client/src/main/java/com/vaadin/v7/client/widget/grid/DataAvailableEvent.java View File

@@ -16,7 +16,7 @@
package com.vaadin.v7.client.widget.grid;

import com.google.gwt.event.shared.GwtEvent;
import com.vaadin.shared.ui.grid.Range;
import com.vaadin.shared.Range;

/**
* Event object describing a change of row availability in DataSource of a Grid.

+ 1
- 1
compatibility-client/src/main/java/com/vaadin/v7/client/widgets/Escalator.java View File

@@ -65,7 +65,7 @@ import com.vaadin.client.DeferredWorker;
import com.vaadin.client.Profiler;
import com.vaadin.client.WidgetUtil;
import com.vaadin.client.ui.SubPartAware;
import com.vaadin.shared.ui.grid.Range;
import com.vaadin.shared.Range;
import com.vaadin.shared.util.SharedUtil;
import com.vaadin.v7.client.widget.escalator.Cell;
import com.vaadin.v7.client.widget.escalator.ColumnConfiguration;

+ 1
- 1
compatibility-client/src/main/java/com/vaadin/v7/client/widgets/Grid.java View File

@@ -90,9 +90,9 @@ import com.vaadin.client.ui.dd.DragAndDropHandler.DragAndDropCallback;
import com.vaadin.client.ui.dd.DragHandle;
import com.vaadin.client.ui.dd.DragHandle.DragHandleCallback;
import com.vaadin.client.widgets.Overlay;
import com.vaadin.shared.Range;
import com.vaadin.shared.Registration;
import com.vaadin.shared.data.sort.SortDirection;
import com.vaadin.shared.ui.grid.Range;
import com.vaadin.shared.util.SharedUtil;
import com.vaadin.v7.client.renderers.ComplexRenderer;
import com.vaadin.v7.client.renderers.Renderer;

+ 1
- 1
compatibility-server/src/main/java/com/vaadin/v7/server/communication/data/RpcDataProviderExtension.java View File

@@ -29,9 +29,9 @@ import java.util.Set;
import com.vaadin.server.AbstractExtension;
import com.vaadin.server.ClientConnector;
import com.vaadin.server.KeyMapper;
import com.vaadin.shared.Range;
import com.vaadin.shared.data.DataProviderRpc;
import com.vaadin.shared.data.DataRequestRpc;
import com.vaadin.shared.ui.grid.Range;
import com.vaadin.v7.data.Container;
import com.vaadin.v7.data.Container.Indexed;
import com.vaadin.v7.data.Container.Indexed.ItemAddEvent;

+ 1
- 1
server/src/main/java/com/vaadin/server/data/DataCommunicator.java View File

@@ -31,10 +31,10 @@ import java.util.stream.Stream;

import com.vaadin.server.AbstractExtension;
import com.vaadin.server.KeyMapper;
import com.vaadin.shared.Range;
import com.vaadin.shared.data.DataCommunicatorClientRpc;
import com.vaadin.shared.data.DataCommunicatorConstants;
import com.vaadin.shared.data.DataRequestRpc;
import com.vaadin.shared.ui.grid.Range;

import elemental.json.Json;
import elemental.json.JsonArray;

shared/src/main/java/com/vaadin/shared/ui/grid/Range.java → shared/src/main/java/com/vaadin/shared/Range.java View File

@@ -14,7 +14,7 @@
* the License.
*/

package com.vaadin.shared.ui.grid;
package com.vaadin.shared;

import java.io.Serializable;


+ 2
- 0
shared/src/test/java/com/vaadin/shared/ui/grid/RangeTest.java View File

@@ -20,6 +20,8 @@ import static org.junit.Assert.assertTrue;

import org.junit.Test;

import com.vaadin.shared.Range;

@SuppressWarnings("static-method")
public class RangeTest {


+ 117
- 0
uitest/src/main/java/com/vaadin/tests/data/DummyData.java View File

@@ -0,0 +1,117 @@
package com.vaadin.tests.data;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;

import com.vaadin.annotations.Widgetset;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.data.DataCommunicator;
import com.vaadin.server.data.ListDataSource;
import com.vaadin.server.data.Query;
import com.vaadin.shared.data.DataCommunicatorConstants;
import com.vaadin.shared.data.selection.SelectionModel;
import com.vaadin.tests.components.AbstractTestUIWithLog;
import com.vaadin.tests.widgetset.TestingWidgetSet;
import com.vaadin.ui.AbstractListing;
import com.vaadin.ui.Button;
import com.vaadin.ui.HorizontalLayout;

@Widgetset(TestingWidgetSet.NAME)
public class DummyData extends AbstractTestUIWithLog {

/**
* DataSource that keeps track on how often the data is requested.
*/
private class LoggingDataSource extends ListDataSource<String> {
private int count = 0;

private LoggingDataSource(Collection<String> collection) {
super(collection);
}

@Override
public Stream<String> apply(Query query) {
log("Backend request #" + (count++));
return super.apply(query);
}
}

/**
* Simplified server only selection model. Selection state passed in data,
* shown as bold text.
*/
private static class DummySelectionModel implements SelectionModel<String> {
private String selected;
private DataCommunicator<String> communicator;

@Override
public Set<String> getSelectedItems() {
if (selected != null) {
return Collections.singleton(selected);
}
return Collections.emptySet();
}

@Override
public void select(String item) {
if (selected != null) {
communicator.refresh(selected);
}
selected = item;
if (selected != null) {
communicator.refresh(selected);
}
}

@Override
public void deselect(String item) {
if (item == selected) {
select(null);
}
}

private void setCommunicator(DataCommunicator<String> dataComm) {
communicator = dataComm;
}
}

public static class DummyComponent
extends AbstractListing<String, DummySelectionModel> {

private DummyComponent() {
setSelectionModel(new DummySelectionModel());
addDataGenerator((str, json) -> {
json.put(DataCommunicatorConstants.DATA, str);
if (isSelected(str)) {
json.put(DataCommunicatorConstants.SELECTED, true);
}
});
getSelectionModel().setCommunicator(getDataCommunicator());
}
}

@Override
protected void setup(VaadinRequest request) {
DummyComponent dummy = new DummyComponent();
List<String> items = new ArrayList<>();
for (int i = 0; i < 300; ++i) {
items.add("Foo " + i);
}
dummy.setDataSource(new LoggingDataSource(items));
dummy.select("Foo 200");

HorizontalLayout controls = new HorizontalLayout();
addComponent(controls);
controls.addComponent(new Button("Select Foo 20", e -> {
dummy.select("Foo " + 20);
}));
controls.addComponent(new Button("Reset data source", e -> {
dummy.setDataSource(new LoggingDataSource(items));
}));
addComponent(dummy);
}
}

+ 43
- 0
uitest/src/main/java/com/vaadin/tests/widgetset/client/data/DummyComponentConnector.java View File

@@ -0,0 +1,43 @@
package com.vaadin.tests.widgetset.client.data;

import com.google.gwt.user.client.ui.FlowPanel;
import com.vaadin.client.connectors.AbstractListingConnector;
import com.vaadin.client.data.DataSource;
import com.vaadin.client.ui.VLabel;
import com.vaadin.shared.data.DataCommunicatorConstants;
import com.vaadin.shared.ui.Connect;
import com.vaadin.tests.data.DummyData.DummyComponent;

import elemental.json.JsonObject;

@Connect(DummyComponent.class)
public class DummyComponentConnector extends AbstractListingConnector {

@Override
public FlowPanel getWidget() {
return (FlowPanel) super.getWidget();
}

@Override
public void setDataSource(DataSource<JsonObject> dataSource) {
super.setDataSource(dataSource);

dataSource.addDataChangeHandler(range -> {
assert range.getStart() == 0 && range.getEnd() == dataSource
.size() : "Widget only supports full updates.";
getWidget().clear();
for (int i = range.getStart(); i < range.getEnd(); ++i) {
VLabel label = new VLabel();
getWidget().add(label);
JsonObject row = dataSource.getRow(i);
String text = row.getString(DataCommunicatorConstants.DATA);
if (row.hasKey(DataCommunicatorConstants.SELECTED)
&& row.getBoolean(DataCommunicatorConstants.SELECTED)) {
text = "<b>" + text + "</b>";
label.addStyleName("selected");
}
label.setHTML(text);
}
});
}
}

+ 1
- 1
uitest/src/test/java/com/vaadin/tests/components/grid/basicfeatures/client/GridDetailsClientTest.java View File

@@ -30,7 +30,7 @@ import org.junit.Test;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebElement;

import com.vaadin.shared.ui.grid.Range;
import com.vaadin.shared.Range;
import com.vaadin.testbench.By;
import com.vaadin.testbench.ElementQuery;
import com.vaadin.testbench.TestBenchElement;

+ 1
- 1
uitest/src/test/java/com/vaadin/tests/components/grid/basicfeatures/escalator/EscalatorSpacerTest.java View File

@@ -33,7 +33,7 @@ import org.openqa.selenium.Keys;
import org.openqa.selenium.WebElement;

import com.vaadin.client.WidgetUtil;
import com.vaadin.shared.ui.grid.Range;
import com.vaadin.shared.Range;
import com.vaadin.testbench.TestBenchElement;
import com.vaadin.testbench.elements.NotificationElement;
import com.vaadin.testbench.parallel.BrowserUtil;

+ 60
- 0
uitest/src/test/java/com/vaadin/tests/data/DummyDataTest.java View File

@@ -0,0 +1,60 @@
package com.vaadin.tests.data;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.util.List;

import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.WebElement;

import com.vaadin.testbench.By;
import com.vaadin.testbench.elements.ButtonElement;
import com.vaadin.tests.tb3.SingleBrowserTest;

public class DummyDataTest extends SingleBrowserTest {

@Before
public void setUp() {
setDebug(true);
openTestURL();
}

@Test
public void testCorrectRowSelectedOnInit() {
List<WebElement> selected = findElements(By.className("selected"));
assertTrue("Only one should be selected", 1 == selected.size());
assertEquals("Wrong item selected", "Foo 200",
selected.get(0).getText());
}

@Test
public void testServerSelectionUpdatesSelected() {
$(ButtonElement.class).first().click();
List<WebElement> selected = findElements(By.className("selected"));
assertTrue("Only one should be selected", 1 == selected.size());
assertEquals("Wrong item selected", "Foo 20",
selected.get(0).getText());
}

@Test
public void testDataUpdateDoesNotCauseBackEndRequest() {
assertEquals("Unexpected backend requests", "2. Backend request #1",
getLogRow(0));
assertEquals("Unexpected backend requests", "1. Backend request #0",
getLogRow(1));
// Select a row on the server-side, triggers an update
$(ButtonElement.class).first().click();
assertEquals("No requests should have happened",
"2. Backend request #1", getLogRow(0));
}

@Test
public void testDataSourceChangeOnlyOneRequest() {
// Change to a new logging data source
$(ButtonElement.class).get(1).click();
assertEquals("DataSource change should only cause 1 request",
"3. Backend request #0", getLogRow(0));
}
}

Loading…
Cancel
Save