import com.vaadin.shared.Connector;
import com.vaadin.shared.data.sort.SortDirection;
import com.vaadin.shared.ui.Connect;
-import com.vaadin.shared.ui.grid.ConnectorIndexChange;
+import com.vaadin.shared.ui.grid.DetailsConnectorChange;
import com.vaadin.shared.ui.grid.EditorClientRpc;
import com.vaadin.shared.ui.grid.EditorServerRpc;
import com.vaadin.shared.ui.grid.GridClientRpc;
}
}
- public void setDetailsConnectorChanges(Set<ConnectorIndexChange> changes) {
+ public void setDetailsConnectorChanges(
+ Set<DetailsConnectorChange> changes) {
/*
* To avoid overwriting connectors while moving them about, we'll
* take all the affected connectors, first all remove those that are
*/
/* Remove moved/removed connectors from bookkeeping */
- for (ConnectorIndexChange change : changes) {
+ for (DetailsConnectorChange change : changes) {
Integer oldIndex = change.getOldIndex();
Connector removedConnector = indexToDetailsMap.remove(oldIndex);
}
/* Add moved/added connectors to bookkeeping */
- for (ConnectorIndexChange change : changes) {
+ for (DetailsConnectorChange change : changes) {
Integer newIndex = change.getNewIndex();
ComponentConnector connector = (ComponentConnector) change
.getConnector();
}
public void responseReceived(int fetchId) {
- boolean success = pendingFetches.remove(fetchId);
- assert success : "Received a response with an unidentified fetch id";
+ /* Ignore negative fetchIds (they're pushed, not fetched) */
+ if (fetchId >= 0) {
+ boolean success = pendingFetches.remove(fetchId);
+ assert success : "Received a response with an unidentified fetch id";
+ }
}
@Override
@Override
public void setDetailsConnectorChanges(
- Set<ConnectorIndexChange> connectorChanges, int fetchId) {
+ Set<DetailsConnectorChange> connectorChanges, int fetchId) {
customDetailsGenerator
.setDetailsConnectorChanges(connectorChanges);
// refresh moved/added details rows
- for (ConnectorIndexChange change : connectorChanges) {
- Integer newIndex = change.getNewIndex();
- if (newIndex != null) {
- int index = newIndex.intValue();
- getWidget().setDetailsVisible(index, false);
- getWidget().setDetailsVisible(index, true);
+ for (DetailsConnectorChange change : connectorChanges) {
+ Integer index = change.getNewIndex();
+ if (index == null) {
+ index = change.getOldIndex();
}
+
+ int i = index.intValue();
+ getWidget().setDetailsVisible(i, false);
+ getWidget().setDetailsVisible(i, true);
}
detailsConnectorFetcher.responseReceived(fetchId);
}
* see GridSpacerUpdater.init for implementation details.
*/
- if (visible && !isDetailsVisible(rowIndex)) {
+ boolean isVisible = isDetailsVisible(rowIndex);
+ if (visible && !isVisible) {
escalator.getBody().setSpacer(rowIndex, DETAILS_ROW_INITIAL_HEIGHT);
visibleDetails.add(rowIndexInteger);
}
- else if (!visible && isDetailsVisible(rowIndex)) {
+ else if (!visible && isVisible) {
escalator.getBody().setSpacer(rowIndex, -1);
visibleDetails.remove(rowIndexInteger);
}
import com.google.gwt.thirdparty.guava.common.collect.BiMap;
import com.google.gwt.thirdparty.guava.common.collect.HashBiMap;
+import com.google.gwt.thirdparty.guava.common.collect.ImmutableSet;
import com.vaadin.data.Container.Indexed;
import com.vaadin.data.Container.Indexed.ItemAddEvent;
import com.vaadin.data.Container.Indexed.ItemRemoveEvent;
public boolean isDetailsVisible(Object itemId) {
return visibleDetails.contains(itemId);
}
+
+ public void refreshDetails() {
+ for (Object itemId : ImmutableSet.copyOf(visibleDetails)) {
+ detailComponentManager.refresh(itemId);
+ }
+ }
}
import com.google.gwt.thirdparty.guava.common.collect.BiMap;
import com.google.gwt.thirdparty.guava.common.collect.HashBiMap;
+import com.google.gwt.thirdparty.guava.common.collect.Maps;
import com.google.gwt.thirdparty.guava.common.collect.Sets;
import com.google.gwt.thirdparty.guava.common.collect.Sets.SetView;
import com.vaadin.data.Container;
import com.vaadin.server.VaadinSession;
import com.vaadin.shared.MouseEventDetails;
import com.vaadin.shared.data.sort.SortDirection;
-import com.vaadin.shared.ui.grid.ConnectorIndexChange;
+import com.vaadin.shared.ui.grid.DetailsConnectorChange;
import com.vaadin.shared.ui.grid.EditorClientRpc;
import com.vaadin.shared.ui.grid.EditorServerRpc;
import com.vaadin.shared.ui.grid.GridClientRpc;
* {@link Collection#isEmpty() empty}, then this field is consistent
* with the connector hierarchy.
*/
- private final Map<Object, Component> visibleDetailsComponents = new HashMap<Object, Component>();
+ private final Map<Object, Component> visibleDetailsComponents = Maps
+ .newHashMap();
/** A lookup map for which row contains which details component. */
private BiMap<Integer, Component> rowIndexToDetails = HashBiMap
* could find out the same thing by taking out all the other components
* and checking whether Grid is their parent or not.
*/
- private final Set<Component> unattachedComponents = new HashSet<Component>();
+ private final Set<Component> unattachedComponents = Sets.newHashSet();
+
+ /**
+ * Keeps tabs on all the details that did not get a component during
+ * {@link #createDetails(Object, int)}.
+ */
+ private final Map<Object, Integer> emptyDetails = Maps.newHashMap();
/**
* Creates a details component by the request of the client side, with
assert itemId != null : "itemId was null";
Integer newRowIndex = Integer.valueOf(rowIndex);
- assert !visibleDetailsComponents.containsKey(itemId) : "itemId already has a component. Should be destroyed first.";
+ assert !visibleDetailsComponents.containsKey(itemId) : "itemId "
+ + "already has a component. Should be destroyed first.";
RowReference rowReference = new RowReference(Grid.this);
rowReference.set(itemId);
Component details = getDetailsGenerator().getDetails(rowReference);
if (details != null) {
-
if (details.getParent() != null) {
String generatorName = getDetailsGenerator().getClass()
.getName();
visibleDetailsComponents.put(itemId, details);
rowIndexToDetails.put(newRowIndex, details);
unattachedComponents.add(details);
+
+ assert !emptyDetails.containsKey(itemId) : "Bookeeping thinks "
+ + "itemId is empty even though we just created a "
+ + "component for it (" + itemId + ")";
+ } else {
+ assert !emptyDetails.containsKey(itemId) : "Bookkeeping has "
+ + "already itemId marked as empty (itemId: " + itemId
+ + ", old index: " + emptyDetails.get(itemId)
+ + ", new index: " + newRowIndex + ")";
+ assert !emptyDetails.containsValue(newRowIndex) : "Bookkeeping"
+ + " already had another itemId for this empty index "
+ + "(index: " + newRowIndex + ", new itemId: " + itemId
+ + ")";
+ emptyDetails.put(itemId, newRowIndex);
}
/*
* the item id for which to destroy the details component
*/
public void destroyDetails(Object itemId) {
+ emptyDetails.remove(itemId);
+
Component removedComponent = visibleDetailsComponents
.remove(itemId);
if (removedComponent == null) {
*
* @return information on how the connectors have changed
*/
- Set<ConnectorIndexChange> getAndResetConnectorChanges() {
- Set<ConnectorIndexChange> changes = new HashSet<ConnectorIndexChange>();
+ Set<DetailsConnectorChange> getAndResetConnectorChanges() {
+ Set<DetailsConnectorChange> changes = new HashSet<DetailsConnectorChange>();
// populate diff with added/changed
for (Entry<Integer, Component> entry : rowIndexToDetails.entrySet()) {
}
if (!SharedUtil.equals(oldIndex, newIndex)) {
- changes.add(new ConnectorIndexChange(component, oldIndex,
+ changes.add(new DetailsConnectorChange(component, oldIndex,
newIndex));
}
}
Component component = entry.getValue();
Integer newIndex = rowIndexToDetails.inverse().get(component);
if (newIndex == null) {
- changes.add(new ConnectorIndexChange(null, oldIndex, null));
+ changes.add(new DetailsConnectorChange(null, oldIndex, null));
}
}
return changes;
}
+
+ public void refresh(Object itemId) {
+ Component component = visibleDetailsComponents.get(itemId);
+ Integer rowIndex = null;
+ if (component != null) {
+ rowIndex = rowIndexToDetails.inverse().get(component);
+ destroyDetails(itemId);
+ } else {
+ rowIndex = emptyDetails.remove(itemId);
+ }
+
+ assert rowIndex != null : "Given itemId does not map to an existing detail row ("
+ + itemId + ")";
+ createDetails(itemId, rowIndex.intValue());
+ }
}
/**
this.detailsGenerator = detailsGenerator;
- getLogger().warning("[[details]] update details on generator swap");
+ datasourceExtension.refreshDetails();
+ getRpcProxy(GridClientRpc.class).setDetailsConnectorChanges(
+ detailComponentManager.getAndResetConnectorChanges(), -1);
}
/**
+++ /dev/null
-/*
- * Copyright 2000-2014 Vaadin Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.vaadin.shared.ui.grid;
-
-import java.io.Serializable;
-
-import com.vaadin.shared.Connector;
-
-/**
- * A description of an indexing modification for a connector. This is used by
- * Grid by internal bookkeeping updates.
- *
- * @since
- * @author Vaadin Ltd
- */
-public class ConnectorIndexChange implements Serializable {
-
- private Connector connector;
- private Integer oldIndex;
- private Integer newIndex;
-
- /** Create a new connector index change */
- public ConnectorIndexChange() {
- }
-
- /**
- * Convenience constructor for setting all the fields in one line.
- * <p>
- * Calling this constructor will also assert that the state of the pojo is
- * consistent by internal assumptions.
- *
- * @param connector
- * the changed connector
- * @param oldIndex
- * the old index
- * @param newIndex
- * the new index
- */
- public ConnectorIndexChange(Connector connector, Integer oldIndex,
- Integer newIndex) {
- this.connector = connector;
- this.oldIndex = oldIndex;
- this.newIndex = newIndex;
-
- assert assertStateIsOk();
- }
-
- private boolean assertStateIsOk() {
- assert (connector != null && newIndex != null)
- || (connector == null && oldIndex != null && newIndex == null) : "connector: "
- + nullityString(connector)
- + ", oldIndex: "
- + nullityString(oldIndex)
- + ", newIndex: "
- + nullityString(newIndex);
- return true;
- }
-
- private static String nullityString(Object object) {
- return object == null ? "null" : "non-null";
- }
-
- /**
- * Gets the old index for the connector.
- * <p>
- * If <code>null</code>, the connector is recently added. This means that
- * {@link #getConnector()} is expected not to return <code>null</code>.
- *
- * @return the old index for the connector
- */
- public Integer getOldIndex() {
- assert assertStateIsOk();
- return oldIndex;
- }
-
- /**
- * Gets the new index for the connector.
- * <p>
- * If <code>null</code>, the connector should be removed. This means that
- * {@link #getConnector()} is expected to return <code>null</code> as well.
- *
- * @return the new index for the connector
- */
- public Integer getNewIndex() {
- assert assertStateIsOk();
- return newIndex;
- }
-
- /**
- * Gets the changed connector.
- *
- * @return the changed connector. Might be <code>null</code>
- */
- public Connector getConnector() {
- assert assertStateIsOk();
- return connector;
- }
-
- /**
- * Sets the changed connector.
- *
- * @param connector
- * the changed connector. May be <code>null</code>
- */
- public void setConnector(Connector connector) {
- this.connector = connector;
- }
-
- /**
- * Sets the old index
- *
- * @param oldIndex
- * the old index. May be <code>null</code> if a new connector is
- * being inserted
- */
- public void setOldIndex(Integer oldIndex) {
- this.oldIndex = oldIndex;
- }
-
- /**
- * Sets the new index
- *
- * @param newIndex
- * the new index. May be <code>null</code> if a connector is
- * being removed
- */
- public void setNewIndex(Integer newIndex) {
- this.newIndex = newIndex;
- }
-}
--- /dev/null
+/*
+ * Copyright 2000-2014 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.shared.ui.grid;
+
+import java.io.Serializable;
+
+import com.vaadin.shared.Connector;
+
+/**
+ * A description of an indexing modification for a connector. This is used by
+ * Grid by internal bookkeeping updates.
+ *
+ * @since
+ * @author Vaadin Ltd
+ */
+public class DetailsConnectorChange implements Serializable {
+
+ private Connector connector;
+ private Integer oldIndex;
+ private Integer newIndex;
+
+ /** Create a new connector index change */
+ public DetailsConnectorChange() {
+ }
+
+ /**
+ * Convenience constructor for setting all the fields in one line.
+ * <p>
+ * Calling this constructor will also assert that the state of the pojo is
+ * consistent by internal assumptions.
+ *
+ * @param connector
+ * the changed connector
+ * @param oldIndex
+ * the old index
+ * @param newIndex
+ * the new index
+ */
+ public DetailsConnectorChange(Connector connector, Integer oldIndex,
+ Integer newIndex) {
+ this.connector = connector;
+ this.oldIndex = oldIndex;
+ this.newIndex = newIndex;
+
+ assert assertStateIsOk();
+ }
+
+ private boolean assertStateIsOk() {
+ boolean connectorAndNewIndexIsNotNull = connector != null
+ && newIndex != null;
+ boolean connectorAndNewIndexIsNullThenOldIndexIsSet = connector == null
+ && newIndex == null && oldIndex != null;
+
+ assert (connectorAndNewIndexIsNotNull || connectorAndNewIndexIsNullThenOldIndexIsSet) : "connector: "
+ + nullityString(connector)
+ + ", oldIndex: "
+ + nullityString(oldIndex)
+ + ", newIndex: "
+ + nullityString(newIndex);
+ return true;
+ }
+
+ private static String nullityString(Object object) {
+ return object == null ? "null" : "non-null";
+ }
+
+ /**
+ * Gets the old index for the connector.
+ * <p>
+ * If <code>null</code>, the connector is recently added. This means that
+ * {@link #getConnector()} is expected not to return <code>null</code>.
+ *
+ * @return the old index for the connector
+ */
+ public Integer getOldIndex() {
+ assert assertStateIsOk();
+ return oldIndex;
+ }
+
+ /**
+ * Gets the new index for the connector.
+ * <p>
+ * If <code>null</code>, the connector should be removed. This means that
+ * {@link #getConnector()} is expected to return <code>null</code> as well.
+ *
+ * @return the new index for the connector
+ */
+ public Integer getNewIndex() {
+ assert assertStateIsOk();
+ return newIndex;
+ }
+
+ /**
+ * Gets the changed connector.
+ *
+ * @return the changed connector. Might be <code>null</code>
+ */
+ public Connector getConnector() {
+ assert assertStateIsOk();
+ return connector;
+ }
+
+ /**
+ * Sets the changed connector.
+ *
+ * @param connector
+ * the changed connector. May be <code>null</code>
+ */
+ public void setConnector(Connector connector) {
+ this.connector = connector;
+ }
+
+ /**
+ * Sets the old index
+ *
+ * @param oldIndex
+ * the old index. May be <code>null</code> if a new connector is
+ * being inserted
+ */
+ public void setOldIndex(Integer oldIndex) {
+ this.oldIndex = oldIndex;
+ }
+
+ /**
+ * Sets the new index
+ *
+ * @param newIndex
+ * the new index. May be <code>null</code> if a connector is
+ * being removed
+ */
+ public void setNewIndex(Integer newIndex) {
+ this.newIndex = newIndex;
+ }
+}
* @param connectorChanges
* the indexing changes of details connectors
* @param fetchId
- * the id of the request for fetching the changes
+ * the id of the request for fetching the changes. A negative
+ * number indicates a push (not requested by the client side)
*/
public void setDetailsConnectorChanges(
- Set<ConnectorIndexChange> connectorChanges, int fetchId);
+ Set<DetailsConnectorChange> connectorChanges, int fetchId);
}
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.Component;
+import com.vaadin.ui.CssLayout;
import com.vaadin.ui.Grid;
import com.vaadin.ui.Grid.CellReference;
import com.vaadin.ui.Grid.CellStyleGenerator;
import com.vaadin.ui.Grid.Column;
+import com.vaadin.ui.Grid.DetailsGenerator;
import com.vaadin.ui.Grid.FooterCell;
import com.vaadin.ui.Grid.HeaderCell;
import com.vaadin.ui.Grid.HeaderRow;
import com.vaadin.ui.Grid.SelectionMode;
import com.vaadin.ui.Grid.SelectionModel;
import com.vaadin.ui.Label;
+import com.vaadin.ui.Notification;
import com.vaadin.ui.Panel;
import com.vaadin.ui.renderers.DateRenderer;
import com.vaadin.ui.renderers.HtmlRenderer;
private Panel detailsPanel;
+ private final DetailsGenerator detailedDetailsGenerator = new DetailsGenerator() {
+ @Override
+ public Component getDetails(final RowReference rowReference) {
+ CssLayout cssLayout = new CssLayout();
+ cssLayout.setHeight("200px");
+ cssLayout.setWidth("100%");
+
+ Item item = rowReference.getItem();
+ for (Object propertyId : item.getItemPropertyIds()) {
+ Property<?> prop = item.getItemProperty(propertyId);
+ String string = prop.getValue().toString();
+ cssLayout.addComponent(new Label(string));
+ }
+
+ final int rowIndex = grid.getContainerDataSource().indexOfId(
+ rowReference.getItemId());
+ ClickListener clickListener = new ClickListener() {
+ @Override
+ public void buttonClick(ClickEvent event) {
+ Notification.show("You clicked on the "
+ + "button in the details for " + "row " + rowIndex);
+ }
+ };
+ cssLayout.addComponent(new Button("Press me", clickListener));
+ return cssLayout;
+ }
+ };
+
+ private final DetailsGenerator watchingDetailsGenerator = new DetailsGenerator() {
+ private int id = 0;
+
+ @Override
+ public Component getDetails(RowReference rowReference) {
+ return new Label("You are watching item id "
+ + rowReference.getItemId() + " (" + (id++) + ")");
+ }
+ };
+
+ private final DetailsGenerator hierarchicalDetailsGenerator = new DetailsGenerator() {
+ @Override
+ public Component getDetails(RowReference rowReference) {
+ detailsPanel = new Panel();
+ detailsPanel.setContent(new Label("One"));
+ return detailsPanel;
+ }
+ };
+
@Override
@SuppressWarnings("unchecked")
protected Grid constructComponent() {
}
private void createDetailsActions() {
- createClickAction("custom details generator", "Details",
- new Command<Grid, Void>() {
- @Override
- public void execute(Grid c, Void value, Object data) {
- grid.setDetailsGenerator(new Grid.DetailsGenerator() {
- private int seq = 0;
+ Command<Grid, DetailsGenerator> swapDetailsGenerator = new Command<Grid, DetailsGenerator>() {
+ @Override
+ public void execute(Grid c, DetailsGenerator generator, Object data) {
+ grid.setDetailsGenerator(generator);
+ }
+ };
- @Override
- public Component getDetails(
- RowReference rowReference) {
- return new Label("You are watching item id "
- + rowReference.getItemId() + " ("
- + (seq++) + ")");
- }
- });
- }
- }, null);
- createClickAction("hierarchy details generator", "Details",
- new Command<Grid, Void>() {
- @Override
- public void execute(Grid c, Void value, Object data) {
- grid.setDetailsGenerator(new Grid.DetailsGenerator() {
- @Override
- public Component getDetails(
- RowReference rowReference) {
- detailsPanel = new Panel();
- detailsPanel.setContent(new Label("One"));
- return detailsPanel;
- }
- });
- }
- }, null);
+ Command<Grid, Boolean> openOrCloseItemId = new Command<Grid, Boolean>() {
+ @Override
+ @SuppressWarnings("boxing")
+ public void execute(Grid g, Boolean visible, Object itemId) {
+ g.setDetailsVisible(itemId, visible);
+ }
+ };
- createClickAction("change hierarchy in generator", "Details",
+ createCategory("Generators", "Details");
+ createClickAction("NULL", "Generators", swapDetailsGenerator,
+ DetailsGenerator.NULL);
+ createClickAction("\"Watching\"", "Generators", swapDetailsGenerator,
+ watchingDetailsGenerator);
+ createClickAction("Detailed", "Generators", swapDetailsGenerator,
+ detailedDetailsGenerator);
+ createClickAction("Hierarchical", "Generators", swapDetailsGenerator,
+ hierarchicalDetailsGenerator);
+
+ createClickAction("- Change Component", "Generators",
new Command<Grid, Void>() {
@Override
public void execute(Grid c, Void value, Object data) {
}
}, null);
- createClickAction("toggle firstItemId", "Details",
+ createClickAction("Toggle firstItemId", "Details",
new Command<Grid, Void>() {
@Override
public void execute(Grid g, Void value, Object data) {
}
}, null);
- createBooleanAction("firstItemId", "Details", false,
- new Command<Grid, Boolean>() {
- @Override
- @SuppressWarnings("boxing")
- public void execute(Grid g, Boolean visible, Object data) {
- g.setDetailsVisible(g.getContainerDataSource()
- .firstItemId(), visible);
- }
- });
+ createBooleanAction("Open firstItemId", "Details", false,
+ openOrCloseItemId, ds.firstItemId());
- createBooleanAction("lastItemId-5", "Details", false,
- new Command<Grid, Boolean>() {
- @Override
- @SuppressWarnings("boxing")
- public void execute(Grid g, Boolean visible, Object data) {
- Object fifthLastItemId = g.getContainerDataSource()
- .getItemIds(ROWS - 6, 1).get(0);
- g.setDetailsVisible(fifthLastItemId, visible);
- }
- });
+ createBooleanAction("Open 995", "Details", false, openOrCloseItemId,
+ ds.getIdByIndex(995));
}
@Override
package com.vaadin.tests.components.grid.basicfeatures.server;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.openqa.selenium.NoSuchElementException;
import com.vaadin.testbench.TestBenchElement;
-import com.vaadin.tests.components.grid.basicfeatures.GridBasicFeatures;
+import com.vaadin.testbench.elements.NotificationElement;
import com.vaadin.tests.components.grid.basicfeatures.GridBasicFeaturesTest;
public class GridDetailsServerTest extends GridBasicFeaturesTest {
* able to scroll that particular details row into view, making tests
* awkward with two scroll commands back to back.
*/
- private static final int ALMOST_LAST_ITEM_INDEX = GridBasicFeatures.ROWS - 5;
- private static final String[] ALMOST_LAST_ITEM_DETAILS = new String[] {
- "Component", "Details", "lastItemId-5" };
-
- private static final String[] FIRST_ITEM_DETAILS = new String[] {
- "Component", "Details", "firstItemId" };
+ private static final int ALMOST_LAST_INDEX = 995;
+ private static final String[] OPEN_ALMOST_LAST_ITEM_DETAILS = new String[] {
+ "Component", "Details", "Open " + ALMOST_LAST_INDEX };
+ private static final String[] OPEN_FIRST_ITEM_DETAILS = new String[] {
+ "Component", "Details", "Open firstItemId" };
private static final String[] TOGGLE_FIRST_ITEM_DETAILS = new String[] {
- "Component", "Details", "toggle firstItemId" };
- private static final String[] CUSTOM_DETAILS_GENERATOR = new String[] {
- "Component", "Details", "custom details generator" };
- private static final String[] HIERARCHY_DETAILS_GENERATOR = new String[] {
- "Component", "Details", "hierarchy details generator" };
+ "Component", "Details", "Toggle firstItemId" };
+ private static final String[] DETAILS_GENERATOR_NULL = new String[] {
+ "Component", "Details", "Generators", "NULL" };
+ private static final String[] DETAILS_GENERATOR_WATCHING = new String[] {
+ "Component", "Details", "Generators", "\"Watching\"" };
+ private static final String[] DETAILS_GENERATOR_HIERARCHICAL = new String[] {
+ "Component", "Details", "Generators", "Hierarchical" };
private static final String[] CHANGE_HIERARCHY = new String[] {
- "Component", "Details", "change hierarchy in generator" };
+ "Component", "Details", "Generators", "- Change Component" };
@Before
public void setUp() {
} catch (NoSuchElementException ignore) {
// expected
}
- selectMenuPath(FIRST_ITEM_DETAILS);
+ selectMenuPath(OPEN_FIRST_ITEM_DETAILS);
assertNotNull("details should've opened", getGridElement()
.getDetails(0));
}
@Test(expected = NoSuchElementException.class)
public void closeVisibleDetails() {
- selectMenuPath(FIRST_ITEM_DETAILS);
- selectMenuPath(FIRST_ITEM_DETAILS);
+ selectMenuPath(OPEN_FIRST_ITEM_DETAILS);
+ selectMenuPath(OPEN_FIRST_ITEM_DETAILS);
getGridElement().getDetails(0);
}
@Test
- public void openDetailsOutsideOfActiveRange() {
+ public void openDetailsOutsideOfActiveRange() throws InterruptedException {
getGridElement().scroll(10000);
- selectMenuPath(FIRST_ITEM_DETAILS);
+ selectMenuPath(OPEN_FIRST_ITEM_DETAILS);
getGridElement().scroll(0);
+ Thread.sleep(50);
assertNotNull("details should've been opened", getGridElement()
.getDetails(0));
}
@Test(expected = NoSuchElementException.class)
public void closeDetailsOutsideOfActiveRange() {
- selectMenuPath(FIRST_ITEM_DETAILS);
+ selectMenuPath(OPEN_FIRST_ITEM_DETAILS);
getGridElement().scroll(10000);
- selectMenuPath(FIRST_ITEM_DETAILS);
+ selectMenuPath(OPEN_FIRST_ITEM_DETAILS);
getGridElement().scroll(0);
getGridElement().getDetails(0);
}
@Test
public void componentIsVisibleClientSide() {
- selectMenuPath(CUSTOM_DETAILS_GENERATOR);
- selectMenuPath(FIRST_ITEM_DETAILS);
+ selectMenuPath(DETAILS_GENERATOR_WATCHING);
+ selectMenuPath(OPEN_FIRST_ITEM_DETAILS);
TestBenchElement details = getGridElement().getDetails(0);
assertNotNull("No widget detected inside details",
}
@Test
- public void togglingAVisibleDetailsRowWithSeparateRoundtrips() {
- selectMenuPath(CUSTOM_DETAILS_GENERATOR);
- selectMenuPath(FIRST_ITEM_DETAILS); // open
- selectMenuPath(FIRST_ITEM_DETAILS); // close
- selectMenuPath(FIRST_ITEM_DETAILS); // open
+ public void openingDetailsTwice() {
+ selectMenuPath(DETAILS_GENERATOR_WATCHING);
+ selectMenuPath(OPEN_FIRST_ITEM_DETAILS); // open
+ selectMenuPath(OPEN_FIRST_ITEM_DETAILS); // close
+ selectMenuPath(OPEN_FIRST_ITEM_DETAILS); // open
TestBenchElement details = getGridElement().getDetails(0);
assertNotNull("No widget detected inside details",
@Test(expected = NoSuchElementException.class)
public void scrollingDoesNotCreateAFloodOfDetailsRows() {
- selectMenuPath(CUSTOM_DETAILS_GENERATOR);
+ selectMenuPath(DETAILS_GENERATOR_WATCHING);
// scroll somewhere to hit uncached rows
getGridElement().scrollToRow(101);
public void openingDetailsOutOfView() {
getGridElement().scrollToRow(500);
- selectMenuPath(CUSTOM_DETAILS_GENERATOR);
- selectMenuPath(FIRST_ITEM_DETAILS);
+ selectMenuPath(DETAILS_GENERATOR_WATCHING);
+ selectMenuPath(OPEN_FIRST_ITEM_DETAILS);
getGridElement().scrollToRow(0);
@Test
public void togglingAVisibleDetailsRowWithOneRoundtrip() {
- selectMenuPath(CUSTOM_DETAILS_GENERATOR);
- selectMenuPath(FIRST_ITEM_DETAILS); // open
+ selectMenuPath(DETAILS_GENERATOR_WATCHING);
+ selectMenuPath(OPEN_FIRST_ITEM_DETAILS); // open
assertTrue("Unexpected generator content",
getGridElement().getDetails(0).getText().endsWith("(0)"));
}
@Test
- @Ignore("This will be patched with https://dev.vaadin.com/review/#/c/7917/")
public void almosLastItemIdIsRendered() {
- selectMenuPath(CUSTOM_DETAILS_GENERATOR);
- selectMenuPath(ALMOST_LAST_ITEM_DETAILS);
+ selectMenuPath(DETAILS_GENERATOR_WATCHING);
+ selectMenuPath(OPEN_ALMOST_LAST_ITEM_DETAILS);
scrollGridVerticallyTo(100000);
TestBenchElement details = getGridElement().getDetails(
- ALMOST_LAST_ITEM_INDEX);
+ ALMOST_LAST_INDEX);
assertNotNull(details);
assertTrue("Unexpected details content",
- details.getText().endsWith(ALMOST_LAST_ITEM_INDEX + " (0)"));
+ details.getText().endsWith(ALMOST_LAST_INDEX + " (0)"));
}
@Test
public void hierarchyChangesWorkInDetails() {
- selectMenuPath(HIERARCHY_DETAILS_GENERATOR);
- selectMenuPath(FIRST_ITEM_DETAILS);
+ selectMenuPath(DETAILS_GENERATOR_HIERARCHICAL);
+ selectMenuPath(OPEN_FIRST_ITEM_DETAILS);
assertEquals("One", getGridElement().getDetails(0).getText());
selectMenuPath(CHANGE_HIERARCHY);
assertEquals("Two", getGridElement().getDetails(0).getText());
}
+ @Ignore("This use case is not currently supported by Grid. If the detail "
+ + "is out of view, the component is detached from the UI and a "
+ + "new instance is generated when scrolled back. Support will "
+ + "maybe be incorporated at a later time")
@Test
- @Ignore("This will be patched with https://dev.vaadin.com/review/#/c/7917/")
public void hierarchyChangesWorkInDetailsWhileOutOfView() {
- selectMenuPath(HIERARCHY_DETAILS_GENERATOR);
- selectMenuPath(FIRST_ITEM_DETAILS);
+ selectMenuPath(DETAILS_GENERATOR_HIERARCHICAL);
+ selectMenuPath(OPEN_FIRST_ITEM_DETAILS);
scrollGridVerticallyTo(10000);
selectMenuPath(CHANGE_HIERARCHY);
scrollGridVerticallyTo(0);
assertEquals("Two", getGridElement().getDetails(0).getText());
}
+
+ @Test
+ public void swappingDetailsGenerators_noDetailsShown() {
+ selectMenuPath(DETAILS_GENERATOR_WATCHING);
+ selectMenuPath(DETAILS_GENERATOR_NULL);
+ assertFalse("Got some errors", $(NotificationElement.class).exists());
+ }
+
+ @Test
+ public void swappingDetailsGenerators_shownDetails() {
+ selectMenuPath(OPEN_FIRST_ITEM_DETAILS);
+ assertTrue("Details should be empty at the start", getGridElement()
+ .getDetails(0).getText().isEmpty());
+
+ selectMenuPath(DETAILS_GENERATOR_WATCHING);
+ assertFalse("Details should not be empty after swapping generator",
+ getGridElement().getDetails(0).getText().isEmpty());
+ }
+
+ @Test
+ public void swappingDetailsGenerators_whileDetailsScrolledOut_showNever() {
+ scrollGridVerticallyTo(1000);
+ selectMenuPath(OPEN_FIRST_ITEM_DETAILS);
+ selectMenuPath(DETAILS_GENERATOR_WATCHING);
+ assertFalse("Got some errors", $(NotificationElement.class).exists());
+ }
+
+ @Test
+ public void swappingDetailsGenerators_whileDetailsScrolledOut_showAfter() {
+ scrollGridVerticallyTo(1000);
+ selectMenuPath(OPEN_FIRST_ITEM_DETAILS);
+ selectMenuPath(DETAILS_GENERATOR_WATCHING);
+ scrollGridVerticallyTo(0);
+
+ assertFalse("Got some errors", $(NotificationElement.class).exists());
+ assertNotNull("Could not find a details", getGridElement()
+ .getDetails(0));
+ }
+
+ @Test
+ public void swappingDetailsGenerators_whileDetailsScrolledOut_showBefore() {
+ selectMenuPath(OPEN_FIRST_ITEM_DETAILS);
+ selectMenuPath(DETAILS_GENERATOR_WATCHING);
+ scrollGridVerticallyTo(1000);
+
+ assertFalse("Got some errors", $(NotificationElement.class).exists());
+ assertNotNull("Could not find a details", getGridElement()
+ .getDetails(0));
+ }
+
+ @Test
+ public void swappingDetailsGenerators_whileDetailsScrolledOut_showBeforeAndAfter() {
+ selectMenuPath(OPEN_FIRST_ITEM_DETAILS);
+ selectMenuPath(DETAILS_GENERATOR_WATCHING);
+ scrollGridVerticallyTo(1000);
+ scrollGridVerticallyTo(0);
+
+ assertFalse("Got some errors", $(NotificationElement.class).exists());
+ assertNotNull("Could not find a details", getGridElement()
+ .getDetails(0));
+ }
+
+ @Test
+ public void nullDetailComponentToggling() {
+ selectMenuPath(OPEN_FIRST_ITEM_DETAILS);
+ selectMenuPath(DETAILS_GENERATOR_WATCHING);
+ selectMenuPath(DETAILS_GENERATOR_NULL);
+ assertTrue("Details should be empty with null component",
+ getGridElement().getDetails(0).getText().isEmpty());
+ selectMenuPath(DETAILS_GENERATOR_WATCHING);
+ assertFalse("Details should be not empty with details component",
+ getGridElement().getDetails(0).getText().isEmpty());
+ }
}