aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTeemu Suo-Anttila <tsuoanttila@users.noreply.github.com>2017-03-23 15:28:37 +0200
committerHenri Sara <henri.sara@gmail.com>2017-03-23 15:28:37 +0200
commit6384bc7dee5ccbaaafbbf569b758b3e6fd8a2c8b (patch)
tree419be55a19b6c4ef65876392b6e31091e1ff5c28
parent17b3f23b0e526250dc134df1f7d2e6cd7f149ba5 (diff)
downloadvaadin-framework-6384bc7dee5ccbaaafbbf569b758b3e6fd8a2c8b.tar.gz
vaadin-framework-6384bc7dee5ccbaaafbbf569b758b3e6fd8a2c8b.zip
Add feature to disable collapsing items in TreeGrid (#8879)
Fixes #8846
-rw-r--r--client/src/main/java/com/vaadin/client/connectors/treegrid/TreeGridConnector.java41
-rw-r--r--client/src/main/java/com/vaadin/client/renderers/HierarchyRenderer.java133
-rw-r--r--documentation/components/components-treegrid.asciidoc16
-rw-r--r--server/src/main/java/com/vaadin/data/provider/HierarchicalDataCommunicator.java28
-rw-r--r--server/src/main/java/com/vaadin/ui/TreeGrid.java58
-rw-r--r--shared/src/main/java/com/vaadin/shared/ui/treegrid/TreeGridCommunicationConstants.java3
-rw-r--r--testbench-api/src/main/java/com/vaadin/testbench/elements/TreeGridElement.java13
-rw-r--r--themes/src/main/themes/VAADIN/themes/valo/components/_treegrid.scss5
-rw-r--r--uitest/src/main/java/com/vaadin/tests/components/treegrid/TreeGridBasicFeatures.java17
-rw-r--r--uitest/src/test/java/com/vaadin/tests/components/treegrid/TreeGridBasicFeaturesTest.java4
-rw-r--r--uitest/src/test/java/com/vaadin/tests/components/treegrid/TreeGridCollapseDisabledTest.java111
-rw-r--r--uitest/src/test/java/com/vaadin/tests/components/treegrid/TreeGridScrollingTest.java4
12 files changed, 346 insertions, 87 deletions
diff --git a/client/src/main/java/com/vaadin/client/connectors/treegrid/TreeGridConnector.java b/client/src/main/java/com/vaadin/client/connectors/treegrid/TreeGridConnector.java
index e96bdffc06..32b5a63840 100644
--- a/client/src/main/java/com/vaadin/client/connectors/treegrid/TreeGridConnector.java
+++ b/client/src/main/java/com/vaadin/client/connectors/treegrid/TreeGridConnector.java
@@ -23,10 +23,8 @@ import com.google.gwt.dom.client.BrowserEvents;
import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.user.client.Event;
-import com.google.web.bindery.event.shared.HandlerRegistration;
import com.vaadin.client.annotations.OnStateChange;
import com.vaadin.client.connectors.grid.GridConnector;
-import com.vaadin.client.renderers.ClickableRenderer;
import com.vaadin.client.renderers.HierarchyRenderer;
import com.vaadin.client.widget.grid.EventCellReference;
import com.vaadin.client.widget.grid.GridEventHandler;
@@ -54,9 +52,6 @@ public class TreeGridConnector extends GridConnector {
private HierarchyRenderer hierarchyRenderer;
- // Expander click event handling
- private HandlerRegistration expanderClickHandlerRegistration;
-
@Override
public TreeGrid getWidget() {
return (TreeGrid) super.getWidget();
@@ -121,7 +116,7 @@ public class TreeGridConnector extends GridConnector {
private HierarchyRenderer getHierarchyRenderer() {
if (hierarchyRenderer == null) {
- hierarchyRenderer = new HierarchyRenderer();
+ hierarchyRenderer = new HierarchyRenderer(this::setCollapsed);
}
return hierarchyRenderer;
}
@@ -130,20 +125,6 @@ public class TreeGridConnector extends GridConnector {
protected void init() {
super.init();
- expanderClickHandlerRegistration = getHierarchyRenderer()
- .addClickHandler(
- new ClickableRenderer.RendererClickHandler<JsonObject>() {
- @Override
- public void onClick(
- ClickableRenderer.RendererClickEvent<JsonObject> event) {
- toggleCollapse(getRowKey(event.getRow()),
- event.getCell().getRowIndex(),
- !isCollapsed(event.getRow()));
- event.stopPropagation();
- event.preventDefault();
- }
- });
-
// Swap Grid's CellFocusEventHandler to this custom one
// The handler is identical to the original one except for the child
// widget check
@@ -158,13 +139,6 @@ public class TreeGridConnector extends GridConnector {
new TreeGridClickEvent(getWidget(), getEventCell(getWidget())));
}
- @Override
- public void onUnregister() {
- super.onUnregister();
-
- expanderClickHandlerRegistration.removeHandler();
- }
-
private native void replaceCellFocusEventHandler(Grid<?> grid,
GridEventHandler<?> eventHandler)
/*-{
@@ -188,9 +162,10 @@ public class TreeGridConnector extends GridConnector {
return cell.getColumn().getRenderer() instanceof HierarchyRenderer;
}
- private void toggleCollapse(String rowKey, int rowIndex, boolean collapse) {
+ private void setCollapsed(int rowIndex, boolean collapsed) {
+ String rowKey = getRowKey(getDataSource().getRow(rowIndex));
getRpcProxy(NodeCollapseRpc.class).setNodeCollapsed(rowKey, rowIndex,
- collapse);
+ collapsed);
}
/**
@@ -273,15 +248,15 @@ public class TreeGridConnector extends GridConnector {
switch (domEvent.getKeyCode()) {
case KeyCodes.KEY_RIGHT:
if (!leaf && collapsed) {
- toggleCollapse(getRowKey(rowData),
- event.getCell().getRowIndex(), true);
+ setCollapsed(event.getCell().getRowIndex(),
+ !collapsed);
}
break;
case KeyCodes.KEY_LEFT:
if (!collapsed) {
// collapse node
- toggleCollapse(getRowKey(rowData),
- event.getCell().getRowIndex(), false);
+ setCollapsed(event.getCell().getRowIndex(),
+ !collapsed);
}
break;
}
diff --git a/client/src/main/java/com/vaadin/client/renderers/HierarchyRenderer.java b/client/src/main/java/com/vaadin/client/renderers/HierarchyRenderer.java
index 205f7929e4..206491f75b 100644
--- a/client/src/main/java/com/vaadin/client/renderers/HierarchyRenderer.java
+++ b/client/src/main/java/com/vaadin/client/renderers/HierarchyRenderer.java
@@ -15,6 +15,8 @@
*/
package com.vaadin.client.renderers;
+import java.util.function.BiConsumer;
+
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.ClickEvent;
@@ -35,21 +37,55 @@ import elemental.json.JsonObject;
/**
* A renderer for displaying hierarchical columns in TreeGrid.
- *
+ *
* @author Vaadin Ltd
* @since 8.1
*/
public class HierarchyRenderer extends ClickableRenderer<Object, Widget> {
-
+
private static final String CLASS_TREE_GRID_NODE = "v-tree-grid-node";
private static final String CLASS_TREE_GRID_EXPANDER = "v-tree-grid-expander";
private static final String CLASS_TREE_GRID_CELL_CONTENT = "v-tree-grid-cell-content";
private static final String CLASS_COLLAPSED = "collapsed";
+ private static final String CLASS_COLLAPSE_DISABLED = "collapse-disabled";
private static final String CLASS_EXPANDED = "expanded";
private static final String CLASS_DEPTH = "depth-";
private Renderer innerRenderer;
+ /**
+ * Constructs a HierarchyRenderer with given collapse callback. Callback is
+ * called when user clicks on the expander of a row. Callback is given the
+ * row index and the target collapsed state.
+ *
+ * @param collapseCallback
+ * the callback for collapsing nodes with row index
+ */
+ public HierarchyRenderer(BiConsumer<Integer, Boolean> collapseCallback) {
+ addClickHandler(event -> {
+ try {
+ JsonObject row = (JsonObject) event.getRow();
+ // Row needs to have hierarchy description
+ if (!hasHierarchyData(row)) {
+ return;
+ }
+
+ JsonObject hierarchyData = getHierarchyData(row);
+ if ((!isCollapsed(hierarchyData)
+ && !isCollapseAllowed(hierarchyData))
+ || isLeaf(hierarchyData)) {
+ return;
+ }
+
+ collapseCallback.accept(event.getCell().getRowIndex(),
+ !isCollapsed(hierarchyData));
+ } finally {
+ event.stopPropagation();
+ event.preventDefault();
+ }
+ });
+ }
+
@Override
public Widget createWidget() {
return new HierarchyItem(CLASS_TREE_GRID_NODE);
@@ -63,18 +99,15 @@ public class HierarchyRenderer extends ClickableRenderer<Object, Widget> {
int depth = 0;
boolean leaf = false;
boolean collapsed = false;
- if (row.hasKey(
- TreeGridCommunicationConstants.ROW_HIERARCHY_DESCRIPTION)) {
- JsonObject rowDescription = row.getObject(
- TreeGridCommunicationConstants.ROW_HIERARCHY_DESCRIPTION);
-
- depth = (int) rowDescription
- .getNumber(TreeGridCommunicationConstants.ROW_DEPTH);
- leaf = rowDescription
- .getBoolean(TreeGridCommunicationConstants.ROW_LEAF);
+ boolean collapseAllowed = true;
+ if (hasHierarchyData(row)) {
+ JsonObject rowDescription = getHierarchyData(row);
+
+ depth = getDepth(rowDescription);
+ leaf = isLeaf(rowDescription);
if (!leaf) {
- collapsed = rowDescription.getBoolean(
- TreeGridCommunicationConstants.ROW_COLLAPSED);
+ collapsed = isCollapsed(rowDescription);
+ collapseAllowed = isCollapseAllowed(rowDescription);
}
}
@@ -89,22 +122,64 @@ public class HierarchyRenderer extends ClickableRenderer<Object, Widget> {
cellWidget.setExpanderState(ExpanderState.EXPANDED);
}
- // Render the contents of the inner renderer. For non widget renderers
- // the cell reference needs to be wrapped so that its getElement method
+ cellWidget.setCollapseAllowed(collapseAllowed);
+
+ // Render the contents of the inner renderer. For non widget
+ // renderers
+ // the cell reference needs to be wrapped so that its getElement
+ // method
// returns the correct element we want to render.
if (innerRenderer instanceof WidgetRenderer) {
- ((WidgetRenderer) innerRenderer).render(cell, data, ((HierarchyItem) widget).content);
+ ((WidgetRenderer) innerRenderer).render(cell, data,
+ ((HierarchyItem) widget).content);
} else {
- innerRenderer.render(new HierarchyRendererCellReferenceWrapper(cell,
- ((HierarchyItem) widget).content.getElement()), data);
+ innerRenderer.render(
+ new HierarchyRendererCellReferenceWrapper(cell,
+ ((HierarchyItem) widget).content.getElement()),
+ data);
}
}
+ private int getDepth(JsonObject rowDescription) {
+ return (int) rowDescription
+ .getNumber(TreeGridCommunicationConstants.ROW_DEPTH);
+ }
+
+ private JsonObject getHierarchyData(JsonObject row) {
+ return row.getObject(
+ TreeGridCommunicationConstants.ROW_HIERARCHY_DESCRIPTION);
+ }
+
+ private boolean hasHierarchyData(JsonObject row) {
+ return row.hasKey(
+ TreeGridCommunicationConstants.ROW_HIERARCHY_DESCRIPTION);
+ }
+
+ private boolean isLeaf(JsonObject rowDescription) {
+ boolean leaf;
+ leaf = rowDescription
+ .getBoolean(TreeGridCommunicationConstants.ROW_LEAF);
+ return leaf;
+ }
+
+ private boolean isCollapseAllowed(JsonObject row) {
+ return row.getBoolean(
+ TreeGridCommunicationConstants.ROW_COLLAPSE_ALLOWED);
+ }
+
+ private boolean isCollapsed(JsonObject rowDescription) {
+ boolean collapsed;
+ collapsed = rowDescription
+ .getBoolean(TreeGridCommunicationConstants.ROW_COLLAPSED);
+ return collapsed;
+ }
+
/**
- * Sets the renderer to be wrapped. This is the original renderer before hierarchy is applied.
+ * Sets the renderer to be wrapped. This is the original renderer before
+ * hierarchy is applied.
*
* @param innerRenderer
- * Renderer to be wrapped.
+ * Renderer to be wrapped.
*/
public void setInnerRenderer(Renderer innerRenderer) {
this.innerRenderer = innerRenderer;
@@ -166,11 +241,13 @@ public class HierarchyRenderer extends ClickableRenderer<Object, Widget> {
}
private void setDepth(int depth) {
- String classNameToBeReplaced = getFullClassName(CLASS_DEPTH, panel.getElement().getClassName());
+ String classNameToBeReplaced = getFullClassName(CLASS_DEPTH,
+ panel.getElement().getClassName());
if (classNameToBeReplaced == null) {
panel.getElement().addClassName(CLASS_DEPTH + depth);
} else {
- panel.getElement().replaceClassName(classNameToBeReplaced, CLASS_DEPTH + depth);
+ panel.getElement().replaceClassName(classNameToBeReplaced,
+ CLASS_DEPTH + depth);
}
}
@@ -178,7 +255,8 @@ public class HierarchyRenderer extends ClickableRenderer<Object, Widget> {
int start = classNameList.indexOf(prefix);
int end = start + prefix.length();
if (start > -1) {
- while (end < classNameList.length() && classNameList.charAt(end) != ' ') {
+ while (end < classNameList.length()
+ && classNameList.charAt(end) != ' ') {
end++;
}
return classNameList.substring(start, end);
@@ -203,6 +281,15 @@ public class HierarchyRenderer extends ClickableRenderer<Object, Widget> {
}
}
+ private void setCollapseAllowed(boolean collapseAllowed) {
+ if (expander.getElement().hasClassName(CLASS_EXPANDED)
+ && !collapseAllowed) {
+ expander.getElement().addClassName(CLASS_COLLAPSE_DISABLED);
+ } else {
+ expander.getElement().removeClassName(CLASS_COLLAPSE_DISABLED);
+ }
+ }
+
private class Expander extends Widget implements HasClickHandlers {
private Expander() {
diff --git a/documentation/components/components-treegrid.asciidoc b/documentation/components/components-treegrid.asciidoc
index d3a431c3e9..f3c2058f03 100644
--- a/documentation/components/components-treegrid.asciidoc
+++ b/documentation/components/components-treegrid.asciidoc
@@ -93,6 +93,22 @@ treeGrid.addColumn(Project::getHoursDone).setCaption("Hours Done");
treeGrid.setHierarchyColumn("name");
----
+== Prevent Node Collapsing
+
+[classname]#TreeGrid# supports setting a callback method that can allow or prevent the user from collapsing an expanded node.
+It can be set with [methodname]#setItemCollapseAllowedProvider# method, that takes a [interfacename]#SerializablePredicate#.
+For nodes that cannot be collapsed, the [literal]#++collapse-disabled++# class name is applied to the expansion element
+
+Avoid doing any heavy operations in the method, since it is called for each item when it is being sent to the client.
+
+Example using a predefined set of persons that can not be collapsed:
+[source, java]
+----
+Set<Person> alwaysExpanded;
+personTreeGrid.setItemCollapseAllowedProvider(person ->
+ !alwaysExpanded.contains(person));
+----
+
== Listening to Events
In addition to supporting all the listeners of the standard [classname]#Grid#, [classname]#TreeGrid# supports listening to the expansion and collapsing of items in its hierarchy.
diff --git a/server/src/main/java/com/vaadin/data/provider/HierarchicalDataCommunicator.java b/server/src/main/java/com/vaadin/data/provider/HierarchicalDataCommunicator.java
index 159dfbe1d4..5bb1335ed0 100644
--- a/server/src/main/java/com/vaadin/data/provider/HierarchicalDataCommunicator.java
+++ b/server/src/main/java/com/vaadin/data/provider/HierarchicalDataCommunicator.java
@@ -28,6 +28,7 @@ import java.util.stream.Stream;
import com.vaadin.data.HierarchyData;
import com.vaadin.data.provider.HierarchyMapper.TreeLevelQuery;
import com.vaadin.server.SerializableConsumer;
+import com.vaadin.server.SerializablePredicate;
import com.vaadin.shared.Range;
import com.vaadin.shared.extension.datacommunicator.HierarchicalDataCommunicatorState;
import com.vaadin.shared.ui.treegrid.TreeGridCommunicationConstants;
@@ -58,6 +59,11 @@ public class HierarchicalDataCommunicator<T> extends DataCommunicator<T> {
private HierarchyMapper mapper = new HierarchyMapper();
/**
+ * Collapse allowed provider used to allow/disallow collapsing nodes.
+ */
+ private SerializablePredicate<T> itemCollapseAllowedProvider = t -> true;
+
+ /**
* The captured client side cache size.
*/
private int latestCacheSize = INITIAL_FETCH_SIZE;
@@ -209,6 +215,9 @@ public class HierarchicalDataCommunicator<T> extends DataCommunicator<T> {
hierarchyData.put(TreeGridCommunicationConstants.ROW_COLLAPSED,
mapper.isCollapsed(key));
hierarchyData.put(TreeGridCommunicationConstants.ROW_LEAF, false);
+ hierarchyData.put(
+ TreeGridCommunicationConstants.ROW_COLLAPSE_ALLOWED,
+ itemCollapseAllowedProvider.test(item));
}
// add hierarchy information to row as metadata
@@ -386,4 +395,23 @@ public class HierarchicalDataCommunicator<T> extends DataCommunicator<T> {
refresh(expandedItem);
}
+ /**
+ * Sets the item collapse allowed provider for this
+ * HierarchicalDataCommunicator. The provider should return {@code true} for
+ * any item that the user can collapse.
+ * <p>
+ * <strong>Note:</strong> This callback will be accessed often when sending
+ * data to the client. The callback should not do any costly operations.
+ *
+ * @param provider
+ * the item collapse allowed provider, not {@code null}
+ */
+ public void setItemCollapseAllowedProvider(
+ SerializablePredicate<T> provider) {
+ Objects.requireNonNull(provider, "Provider can't be null");
+ itemCollapseAllowedProvider = provider;
+
+ getActiveDataHandler().getActiveData().forEach(this::refresh);
+ }
+
}
diff --git a/server/src/main/java/com/vaadin/ui/TreeGrid.java b/server/src/main/java/com/vaadin/ui/TreeGrid.java
index 2378ff3f2b..6f961990de 100644
--- a/server/src/main/java/com/vaadin/ui/TreeGrid.java
+++ b/server/src/main/java/com/vaadin/ui/TreeGrid.java
@@ -35,6 +35,7 @@ import com.vaadin.data.provider.HierarchicalDataCommunicator;
import com.vaadin.data.provider.HierarchicalDataProvider;
import com.vaadin.data.provider.HierarchicalQuery;
import com.vaadin.data.provider.InMemoryHierarchicalDataProvider;
+import com.vaadin.server.SerializablePredicate;
import com.vaadin.shared.Registration;
import com.vaadin.shared.ui.treegrid.NodeCollapseRpc;
import com.vaadin.shared.ui.treegrid.TreeGridState;
@@ -58,7 +59,7 @@ public class TreeGrid<T> extends Grid<T> {
/**
* Item expand event listener.
- *
+ *
* @author Vaadin Ltd
* @since 8.1
* @param <T>
@@ -72,7 +73,7 @@ public class TreeGrid<T> extends Grid<T> {
/**
* Callback method for when an item has been expanded.
- *
+ *
* @param event
* the expand event
*/
@@ -81,7 +82,7 @@ public class TreeGrid<T> extends Grid<T> {
/**
* Item collapse event listener.
- *
+ *
* @author Vaadin Ltd
* @since 8.1
* @param <T>
@@ -95,7 +96,7 @@ public class TreeGrid<T> extends Grid<T> {
/**
* Callback method for when an item has been collapsed.
- *
+ *
* @param event
* the collapse event
*/
@@ -104,7 +105,7 @@ public class TreeGrid<T> extends Grid<T> {
/**
* An event that is fired when an item is expanded.
- *
+ *
* @author Vaadin Ltd
* @since 8.1
* @param <T>
@@ -116,7 +117,7 @@ public class TreeGrid<T> extends Grid<T> {
/**
* Construct an expand event.
- *
+ *
* @param source
* the tree grid this event originated from
* @param item
@@ -129,7 +130,7 @@ public class TreeGrid<T> extends Grid<T> {
/**
* Get the expanded item that triggered this event.
- *
+ *
* @return the expanded item
*/
public T getExpandedItem() {
@@ -140,7 +141,7 @@ public class TreeGrid<T> extends Grid<T> {
/**
* An event that is fired when an item is collapsed. Note that expanded
* subtrees of the collapsed item will not trigger collapse events.
- *
+ *
* @author Vaadin Ltd
* @since 8.1
* @param <T>
@@ -152,7 +153,7 @@ public class TreeGrid<T> extends Grid<T> {
/**
* Construct a collapse event.
- *
+ *
* @param source
* the tree grid this event originated from
* @param item
@@ -165,7 +166,7 @@ public class TreeGrid<T> extends Grid<T> {
/**
* Get the collapsed item that triggered this event.
- *
+ *
* @return the collapsed item
*/
public T getCollapsedItem() {
@@ -195,9 +196,9 @@ public class TreeGrid<T> extends Grid<T> {
/**
* Adds an ExpandListener to this TreeGrid.
- *
+ *
* @see ExpandEvent
- *
+ *
* @param listener
* the listener to add
* @return a registration for the listener
@@ -209,9 +210,9 @@ public class TreeGrid<T> extends Grid<T> {
/**
* Adds a CollapseListener to this TreeGrid.
- *
+ *
* @see CollapseEvent
- *
+ *
* @param listener
* the listener to add
* @return a registration for the listener
@@ -366,6 +367,26 @@ public class TreeGrid<T> extends Grid<T> {
getState().hierarchyColumnId = getInternalIdForColumn(getColumn(id));
}
+ /**
+ * Sets the item collapse allowed provider for this TreeGrid. The provider
+ * should return {@code true} for any item that the user can collapse.
+ * <p>
+ * <strong>Note:</strong> This callback will be accessed often when sending
+ * data to the client. The callback should not do any costly operations.
+ * <p>
+ * This method is a shortcut to method with the same name in
+ * {@link HierarchicalDataCommunicator}.
+ *
+ * @param provider
+ * the item collapse allowed provider, not {@code null}
+ *
+ * @see HierarchicalDataCommunicator#setItemCollapseAllowedProvider(SerializablePredicate)
+ */
+ public void setItemCollapseAllowedProvider(
+ SerializablePredicate<T> provider) {
+ getDataCommunicator().setItemCollapseAllowedProvider(provider);
+ }
+
@Override
protected TreeGridState getState() {
return (TreeGridState) super.getState();
@@ -464,9 +485,8 @@ public class TreeGrid<T> extends Grid<T> {
.map(DesignFormatter::encodeForTextNode)
.orElse(""));
}
- getDataProvider().fetch(new HierarchicalQuery<>(null, item))
- .forEach(childItem -> writeRow(container, childItem, item,
- context));
+ getDataProvider().fetch(new HierarchicalQuery<>(null, item)).forEach(
+ childItem -> writeRow(container, childItem, item, context));
}
@Override
@@ -491,7 +511,7 @@ public class TreeGrid<T> extends Grid<T> {
/**
* Emit an expand event.
- *
+ *
* @param item
* the item that was expanded
*/
@@ -501,7 +521,7 @@ public class TreeGrid<T> extends Grid<T> {
/**
* Emit a collapse event.
- *
+ *
* @param item
* the item that was collapsed
*/
diff --git a/shared/src/main/java/com/vaadin/shared/ui/treegrid/TreeGridCommunicationConstants.java b/shared/src/main/java/com/vaadin/shared/ui/treegrid/TreeGridCommunicationConstants.java
index c3bd0bbd91..0e678aeb3a 100644
--- a/shared/src/main/java/com/vaadin/shared/ui/treegrid/TreeGridCommunicationConstants.java
+++ b/shared/src/main/java/com/vaadin/shared/ui/treegrid/TreeGridCommunicationConstants.java
@@ -20,7 +20,7 @@ import java.io.Serializable;
/**
* Set of contants used by TreeGrid. These are commonly used JsonObject keys
* which are considered to be reserved for internal use.
- *
+ *
* @author Vaadin Ltd
* @since 8.1
*/
@@ -29,4 +29,5 @@ public class TreeGridCommunicationConstants implements Serializable {
public static final String ROW_DEPTH = "d";
public static final String ROW_COLLAPSED = "c";
public static final String ROW_LEAF = "l";
+ public static final String ROW_COLLAPSE_ALLOWED = "ca";
}
diff --git a/testbench-api/src/main/java/com/vaadin/testbench/elements/TreeGridElement.java b/testbench-api/src/main/java/com/vaadin/testbench/elements/TreeGridElement.java
index 0ca81a56ef..ed02041e06 100644
--- a/testbench-api/src/main/java/com/vaadin/testbench/elements/TreeGridElement.java
+++ b/testbench-api/src/main/java/com/vaadin/testbench/elements/TreeGridElement.java
@@ -15,6 +15,9 @@
*/
package com.vaadin.testbench.elements;
+import java.util.Arrays;
+import java.util.List;
+
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebElement;
@@ -97,8 +100,9 @@ public class TreeGridElement extends GridElement {
public boolean isRowExpanded(int rowIndex, int hierarchyColumnIndex) {
WebElement expandElement = getExpandElement(rowIndex,
hierarchyColumnIndex);
- return expandElement.getAttribute("expanded") != null
- && expandElement.getAttribute("collapsed") == null;
+ List<String> classes = Arrays
+ .asList(expandElement.getAttribute("class").split(" "));
+ return classes.contains("expanded") && !classes.contains("collapsed");
}
/**
@@ -111,10 +115,7 @@ public class TreeGridElement extends GridElement {
* @return {@code true} if collapsed, {@code false} if expanded
*/
public boolean isRowCollapsed(int rowIndex, int hierarchyColumnIndex) {
- WebElement expandElement = getExpandElement(rowIndex,
- hierarchyColumnIndex);
- return expandElement.getAttribute("collapsed") != null
- && expandElement.getAttribute("expanded") == null;
+ return !isRowExpanded(rowIndex, hierarchyColumnIndex);
}
/**
diff --git a/themes/src/main/themes/VAADIN/themes/valo/components/_treegrid.scss b/themes/src/main/themes/VAADIN/themes/valo/components/_treegrid.scss
index 15c53f8cc9..8255d830eb 100644
--- a/themes/src/main/themes/VAADIN/themes/valo/components/_treegrid.scss
+++ b/themes/src/main/themes/VAADIN/themes/valo/components/_treegrid.scss
@@ -42,6 +42,11 @@ $tg-expander-padding: 10px !default;
content: $tg-expander-char-collapsed;
}
}
+
+ &.collapse-disabled {
+ @include opacity($v-disabled-opacity);
+ cursor: default;
+ }
}
// Hierarchy depth styling
diff --git a/uitest/src/main/java/com/vaadin/tests/components/treegrid/TreeGridBasicFeatures.java b/uitest/src/main/java/com/vaadin/tests/components/treegrid/TreeGridBasicFeatures.java
index 3b58e6c75e..9b07cc106e 100644
--- a/uitest/src/main/java/com/vaadin/tests/components/treegrid/TreeGridBasicFeatures.java
+++ b/uitest/src/main/java/com/vaadin/tests/components/treegrid/TreeGridBasicFeatures.java
@@ -9,6 +9,7 @@ import com.vaadin.annotations.Widgetset;
import com.vaadin.data.HierarchyData;
import com.vaadin.data.provider.DataProvider;
import com.vaadin.data.provider.InMemoryHierarchicalDataProvider;
+import com.vaadin.server.SerializablePredicate;
import com.vaadin.tests.components.AbstractComponentTest;
import com.vaadin.ui.TreeGrid;
@@ -38,7 +39,8 @@ public class TreeGridBasicFeatures extends AbstractComponentTest<TreeGrid> {
grid.addColumn(HierarchicalTestBean::toString).setCaption("String")
.setId("string");
grid.addColumn(HierarchicalTestBean::getDepth).setCaption("Depth")
- .setId("depth");
+ .setId("depth").setDescriptionGenerator(
+ t -> "Hierarchy depth: " + t.getDepth());
grid.addColumn(HierarchicalTestBean::getIndex)
.setCaption("Index on this depth").setId("index");
grid.setHierarchyColumn("string");
@@ -54,6 +56,7 @@ public class TreeGridBasicFeatures extends AbstractComponentTest<TreeGrid> {
createDataProviderSelect();
createHierarchyColumnSelect();
+ createCollapseAllowedSelect();
createListenerMenu();
}
@@ -103,6 +106,18 @@ public class TreeGridBasicFeatures extends AbstractComponentTest<TreeGrid> {
(treeGrid, value, data) -> treeGrid.setHierarchyColumn(value));
}
+ private void createCollapseAllowedSelect() {
+ LinkedHashMap<String, SerializablePredicate<HierarchicalTestBean>> options = new LinkedHashMap<>();
+ options.put("all allowed", t -> true);
+ options.put("all disabled", t -> false);
+ options.put("depth 0 disabled", t -> t.getDepth() != 0);
+ options.put("depth 1 disabled", t -> t.getDepth() != 1);
+
+ createSelectAction("Collapse allowed", CATEGORY_FEATURES, options,
+ "all allowed", (treeGrid, value, data) -> treeGrid
+ .setItemCollapseAllowedProvider(value));
+ }
+
@SuppressWarnings("unchecked")
private void createListenerMenu() {
createListenerAction("Collapse listener", "State",
diff --git a/uitest/src/test/java/com/vaadin/tests/components/treegrid/TreeGridBasicFeaturesTest.java b/uitest/src/test/java/com/vaadin/tests/components/treegrid/TreeGridBasicFeaturesTest.java
index 215bef95d2..cda2ba5966 100644
--- a/uitest/src/test/java/com/vaadin/tests/components/treegrid/TreeGridBasicFeaturesTest.java
+++ b/uitest/src/test/java/com/vaadin/tests/components/treegrid/TreeGridBasicFeaturesTest.java
@@ -127,7 +127,7 @@ public class TreeGridBasicFeaturesTest extends MultiBrowserTest {
Assert.assertFalse(logContainsText("Item expanded: 0 | 0"));
Assert.assertFalse(logContainsText("Item collapsed: 0 | 0"));
- grid.collapseWithClick(0);
+ grid.expandWithClick(0);
Assert.assertTrue(logContainsText("Item expanded: 0 | 0"));
Assert.assertFalse(logContainsText("Item collapsed: 0 | 0"));
@@ -140,7 +140,7 @@ public class TreeGridBasicFeaturesTest extends MultiBrowserTest {
selectMenuPath("Component", "State", "Expand listener");
selectMenuPath("Component", "State", "Collapse listener");
- grid.collapseWithClick(1);
+ grid.expandWithClick(1);
grid.collapseWithClick(1);
Assert.assertFalse(logContainsText("Item expanded: 0 | 1"));
diff --git a/uitest/src/test/java/com/vaadin/tests/components/treegrid/TreeGridCollapseDisabledTest.java b/uitest/src/test/java/com/vaadin/tests/components/treegrid/TreeGridCollapseDisabledTest.java
new file mode 100644
index 0000000000..dbb8e38b2a
--- /dev/null
+++ b/uitest/src/test/java/com/vaadin/tests/components/treegrid/TreeGridCollapseDisabledTest.java
@@ -0,0 +1,111 @@
+package com.vaadin.tests.components.treegrid;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.vaadin.testbench.elements.TreeGridElement;
+import com.vaadin.tests.tb3.SingleBrowserTest;
+
+public class TreeGridCollapseDisabledTest extends SingleBrowserTest {
+
+ private TreeGridElement grid;
+
+ @Override
+ protected Class<?> getUIClass() {
+ return TreeGridBasicFeatures.class;
+ }
+
+ @Before
+ public void before() {
+ openTestURL();
+ grid = $(TreeGridElement.class).first();
+ }
+
+ @Test
+ public void collapse_disabled_for_all() {
+ selectMenuPath("Component", "Features", "Collapse allowed",
+ "all disabled");
+
+ // Assert first and second row can be expanded, but not collapsed
+ assertExpandRow(0);
+ assertCollapseRowDisabled(0);
+
+ assertExpandRow(1);
+ assertCollapseRowDisabled(1);
+ }
+
+ @Test
+ public void collapse_disabled_for_depth0() {
+ selectMenuPath("Component", "Features", "Collapse allowed",
+ "depth 0 disabled");
+
+ // Assert first row expands
+ assertExpandRow(0);
+
+ // Assert second row expands and collapses
+ assertExpandRow(1);
+ assertCollapseRow(1);
+
+ // Assert first row does not collapse
+ assertCollapseRowDisabled(0);
+ }
+
+ @Test
+ public void collapse_disabled_for_depth1() {
+ selectMenuPath("Component", "Features", "Collapse allowed",
+ "depth 1 disabled");
+
+ // Assert first row expands
+ assertExpandRow(0);
+
+ // Assert second row expands but does not collapse
+ assertExpandRow(1);
+ assertCollapseRowDisabled(1);
+
+ // Assert first row still collapses
+ assertCollapseRow(0);
+ }
+
+ @Test
+ public void collapse_disabled_mode_change_with_expanded_rows() {
+ // Assert first row expands
+ assertExpandRow(0);
+
+ // Assert second row expands and collapses
+ assertExpandRow(1);
+ assertCollapseRow(1);
+
+ selectMenuPath("Component", "Features", "Collapse allowed",
+ "depth 1 disabled");
+
+ Assert.assertTrue("First row should still be expanded",
+ grid.isRowExpanded(0, 0));
+
+ // Assert second row expands but does not collapse
+ assertExpandRow(1);
+ assertCollapseRowDisabled(1);
+
+ // Assert first row still collapses
+ assertCollapseRow(0);
+ }
+
+ private void assertExpandRow(int row) {
+ Assert.assertFalse(grid.isRowExpanded(row, 0));
+ grid.expandWithClick(row);
+ Assert.assertTrue(grid.isRowExpanded(row, 0));
+ }
+
+ private void assertCollapseRow(int row) {
+ Assert.assertTrue("Row not expanded", grid.isRowExpanded(row, 0));
+ grid.collapseWithClick(row);
+ Assert.assertFalse("Row did not collapse", grid.isRowExpanded(row, 0));
+ }
+
+ private void assertCollapseRowDisabled(int row) {
+ Assert.assertTrue("Row not expanded", grid.isRowExpanded(row, 0));
+ grid.collapseWithClick(row);
+ Assert.assertTrue("Row should not collapse",
+ grid.isRowExpanded(row, 0));
+ }
+}
diff --git a/uitest/src/test/java/com/vaadin/tests/components/treegrid/TreeGridScrollingTest.java b/uitest/src/test/java/com/vaadin/tests/components/treegrid/TreeGridScrollingTest.java
index be7953ba35..b4bc65e1ce 100644
--- a/uitest/src/test/java/com/vaadin/tests/components/treegrid/TreeGridScrollingTest.java
+++ b/uitest/src/test/java/com/vaadin/tests/components/treegrid/TreeGridScrollingTest.java
@@ -129,7 +129,7 @@ public class TreeGridScrollingTest extends SingleBrowserTest {
verifyRow(1, 1, 0);
verifyRow(0, 0, 0);
- grid.expandWithClick(3);
+ grid.collapseWithClick(3);
verifyRow(0, 0, 0);
verifyRow(1, 1, 0);
@@ -144,7 +144,7 @@ public class TreeGridScrollingTest extends SingleBrowserTest {
verifyRow(1, 1, 0);
verifyRow(0, 0, 0);
- grid.expandWithClick(0);
+ grid.collapseWithClick(0);
verifyRow(0, 0, 0);
verifyRow(10, 0, 10);