Browse Source

Enable row height and content mode settings for Tree (#9540)

* Enable row height setting for Tree
* Add content mode for captions
* Align expander element by default to top

The content mode allows use of preformatted and
HTML captions that bring value to row height

Fixes #9411
tags/8.1.0.beta3
Teemu Suo-Anttila 6 years ago
parent
commit
f700c01e12

+ 25
- 4
client/src/main/java/com/vaadin/client/connectors/grid/TreeRendererConnector.java View File

@@ -17,6 +17,7 @@ package com.vaadin.client.connectors.grid;

import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.user.client.Element;
import com.vaadin.client.annotations.OnStateChange;
import com.vaadin.client.renderers.HtmlRenderer;
import com.vaadin.client.renderers.Renderer;
import com.vaadin.client.widget.grid.RendererCellReference;
@@ -27,7 +28,7 @@ import com.vaadin.ui.Tree.TreeRenderer;
import elemental.json.JsonObject;

/**
* Connector for TreeRenderer
* Connector for TreeRenderer.
*
* @author Vaadin Ltd
* @since 8.1
@@ -42,9 +43,8 @@ public class TreeRendererConnector

@Override
public void render(RendererCellReference cell, String htmlString) {
String content = "<span class=\"v-captiontext\">" +
SafeHtmlUtils.htmlEscape(htmlString)
+ "</span>";
String content = "<span class=\"v-captiontext\">"
+ getContentString(htmlString) + "</span>";

JsonObject row = getParent().getParent().getDataSource()
.getRow(cell.getRowIndex());
@@ -56,9 +56,30 @@ public class TreeRendererConnector
}
super.render(cell, content);
}

private String getContentString(String htmlString) {
switch (getState().mode) {
case HTML:
return htmlString;
case PREFORMATTED:
return "<pre>" + SafeHtmlUtils.htmlEscape(htmlString)
+ "</pre>";
default:
return SafeHtmlUtils.htmlEscape(htmlString);
}
}
};
}

@OnStateChange("mode")
void updateContentMode() {
// Redraw content
getParent().getParent().getWidget().requestRefreshBody();

// Some pre-formatted content might change size of content.
getParent().getParent().getWidget().recalculateColumnWidths();
}

@Override
public ColumnConnector getParent() {
return (ColumnConnector) super.getParent();

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

@@ -6381,8 +6381,10 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>,

/**
* Request delayed refresh of all body rows.
*
* @since 8.1
*/
private void requestRefreshBody() {
public void requestRefreshBody() {
if (!refreshBodyRequested) {
refreshBodyRequested = true;
Scheduler.get().scheduleFinally(() -> {

+ 23
- 1
server/src/main/java/com/vaadin/ui/Tree.java View File

@@ -218,13 +218,14 @@ public class Tree<T> extends Composite
private TreeGrid<T> treeGrid = new TreeGrid<>();
private ItemCaptionGenerator<T> captionGenerator = String::valueOf;
private IconGenerator<T> iconProvider = t -> null;
private final TreeRenderer renderer;

/**
* Constructs a new Tree Component.
*/
public Tree() {
setCompositionRoot(treeGrid);
TreeRenderer renderer = new TreeRenderer();
renderer = new TreeRenderer();
treeGrid.getDataCommunicator().addDataGenerator(renderer);
treeGrid.addColumn(i -> captionGenerator.apply(i), renderer)
.setId("column");
@@ -762,4 +763,25 @@ public class Tree<T> extends Composite
public void setComponentError(ErrorMessage componentError) {
treeGrid.setComponentError(componentError);
}

/**
* Sets the height of a row. If -1 (default), the row height is calculated
* based on the theme for an empty row before the Tree is displayed.
*
* @param rowHeight
* The height of a row in pixels or -1 for automatic calculation
*/
public void setRowHeight(double rowHeight) {
treeGrid.setRowHeight(rowHeight);
}

/**
* Sets the content mode of the item caption.
*
* @param contentMode
* the content mode
*/
public void setContentMode(ContentMode contentMode) {
renderer.getState().mode = contentMode;
}
}

+ 2
- 0
shared/src/main/java/com/vaadin/shared/ui/tree/TreeRendererState.java View File

@@ -15,6 +15,7 @@
*/
package com.vaadin.shared.ui.tree;

import com.vaadin.shared.ui.ContentMode;
import com.vaadin.shared.ui.grid.renderers.AbstractRendererState;

/**
@@ -25,4 +26,5 @@ import com.vaadin.shared.ui.grid.renderers.AbstractRendererState;
*/
public class TreeRendererState extends AbstractRendererState {

public ContentMode mode = ContentMode.TEXT;
}

+ 4
- 0
themes/src/main/themes/VAADIN/themes/valo/components/_treegrid.scss View File

@@ -27,6 +27,9 @@ $v-treegrid-class-depth: depth !default;


.#{$primary-stylename}-expander {
display: inline-block;
vertical-align: top;

&::before {
display: inline-block;
width: $v-treegrid-expander-width;
@@ -62,6 +65,7 @@ $v-treegrid-class-depth: depth !default;
// Expander and cell content in same line
.#{$primary-stylename}-cell-content {
display: inline-block;
vertical-align: middle;
}

.#{$primary-stylename}-row-focused {

+ 36
- 8
uitest/src/main/java/com/vaadin/tests/components/tree/TreeBasicFeatures.java View File

@@ -1,7 +1,11 @@
package com.vaadin.tests.components.tree;

import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import com.vaadin.annotations.Theme;
import com.vaadin.annotations.Widgetset;
@@ -12,21 +16,24 @@ import com.vaadin.server.ClassResource;
import com.vaadin.server.ThemeResource;
import com.vaadin.server.VaadinRequest;
import com.vaadin.shared.Registration;
import com.vaadin.shared.ui.ContentMode;
import com.vaadin.tests.components.AbstractTestUIWithLog;
import com.vaadin.tests.data.bean.HierarchicalTestBean;
import com.vaadin.ui.Component;
import com.vaadin.ui.Grid.SelectionMode;
import com.vaadin.ui.IconGenerator;
import com.vaadin.ui.MenuBar;
import com.vaadin.ui.MenuBar.Command;
import com.vaadin.ui.MenuBar.MenuItem;
import com.vaadin.ui.Tree;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Grid.SelectionMode;

@Theme("tests-valo-disabled-animations")
@Widgetset("com.vaadin.DefaultWidgetSet")
public class TreeBasicFeatures extends AbstractTestUIWithLog {

public static final double[] ROW_HEIGHTS = new double[] { 35.5d, 72.78d };

private Tree<HierarchicalTestBean> tree;
private TreeDataProvider<HierarchicalTestBean> inMemoryDataProvider;
private IconGenerator<HierarchicalTestBean> iconGenerator = i -> {
@@ -69,7 +76,9 @@ public class TreeBasicFeatures extends AbstractTestUIWithLog {
MenuItem componentMenu = menu.addItem("Component", null);
createIconMenu(componentMenu.addItem("Icons", null));
createCaptionMenu(componentMenu.addItem("Captions", null));
createContentModeMenu(componentMenu.addItem("ContentMode", null));
createSelectionModeMenu(componentMenu.addItem("Selection Mode", null));
createRowHeightMenu(componentMenu.addItem("Row Height", null));
componentMenu.addItem("Item Click Listener", new Command() {

private Registration registration;
@@ -110,6 +119,12 @@ public class TreeBasicFeatures extends AbstractTestUIWithLog {
return menu;
}

private void createRowHeightMenu(MenuItem rowHeightMenu) {
Stream.concat(Stream.of(-1d), Arrays.stream(ROW_HEIGHTS).boxed())
.forEach(height -> rowHeightMenu.addItem(String.valueOf(height),
item -> tree.setRowHeight(height)));
}

private void createSelectionModeMenu(MenuItem modeMenu) {
for (SelectionMode mode : SelectionMode.values()) {
modeMenu.addItem(mode.name(), item -> tree.setSelectionMode(mode));
@@ -117,13 +132,26 @@ public class TreeBasicFeatures extends AbstractTestUIWithLog {
}

private void createCaptionMenu(MenuItem captionMenu) {
captionMenu.addItem("String.valueOf",
menu -> tree.setItemCaptionGenerator(String::valueOf));
captionMenu
.addItem("Custom caption",
menu -> tree.setItemCaptionGenerator(i -> "Id: "
+ i.getId() + ", Depth: " + i.getDepth()
+ ", Index: " + i.getIndex()));
captionMenu.addItem("String.valueOf", menu -> {
tree.setItemCaptionGenerator(String::valueOf);
tree.setContentMode(ContentMode.TEXT);
});
captionMenu.addItem("Custom caption", menu -> {
tree.setItemCaptionGenerator(i -> "Id: " + i.getId() + "\nDepth: "
+ i.getDepth() + ", Index: " + i.getIndex());
tree.setContentMode(ContentMode.PREFORMATTED);
});
captionMenu.addItem("HTML caption", menu -> {
tree.setItemCaptionGenerator(
i -> "Id: " + i.getId() + "<br/>Depth: " + i.getDepth()
+ "<br/>Index: " + i.getIndex());
tree.setContentMode(ContentMode.HTML);
});
}

private void createContentModeMenu(MenuItem contentModeMenu) {
Arrays.stream(ContentMode.values()).forEach(mode -> contentModeMenu
.addItem(mode.toString(), item -> tree.setContentMode(mode)));
}

private void createIconMenu(MenuItem iconMenu) {

+ 56
- 7
uitest/src/test/java/com/vaadin/tests/components/tree/TreeBasicFeaturesTest.java View File

@@ -1,6 +1,7 @@
package com.vaadin.tests.components.tree;

import java.io.IOException;
import java.util.Arrays;
import java.util.function.Predicate;

import org.junit.Assert;
@@ -92,7 +93,8 @@ public class TreeBasicFeaturesTest extends MultiBrowserTest {

for (int j = 0; j < 3; ++j) {
item = tree.getItem(n++);
Assert.assertEquals((shouldHaveIcon ? "\ue92d" : "") + "1 | " + j,
Assert.assertEquals(
(shouldHaveIcon ? "\ue92d" : "") + "1 | " + j,
item.getText());

Assert.assertEquals("Unexpected icon state", shouldHaveIcon,
@@ -111,26 +113,61 @@ public class TreeBasicFeaturesTest extends MultiBrowserTest {

@Test
public void tree_custom_caption() {
// Set row height big enough to show whole content.
selectMenuPath("Component", "Row Height",
String.valueOf(TreeBasicFeatures.ROW_HEIGHTS[1]));

selectMenuPath("Component", "Captions", "Custom caption");
TreeElement tree = $(TreeElement.class).first();
Assert.assertEquals("Id: /0/0, Depth: 0, Index: 0",
Assert.assertEquals("Id: /0/0\nDepth: 0, Index: 0",
tree.getItem(0).getText());
Assert.assertEquals("Id: /0/1, Depth: 0, Index: 1",
Assert.assertEquals("Id: /0/1\nDepth: 0, Index: 1",
tree.getItem(1).getText());
tree.expand(0);
Assert.assertEquals("Id: /0/0/1/0, Depth: 1, Index: 0",
Assert.assertEquals("Id: /0/0/1/0\nDepth: 1, Index: 0",
tree.getItem(1).getText());
Assert.assertEquals("Id: /0/0/1/1, Depth: 1, Index: 1",
Assert.assertEquals("Id: /0/0/1/1\nDepth: 1, Index: 1",
tree.getItem(2).getText());
tree.expand(1);
Assert.assertEquals("Id: /0/0/1/0/2/0, Depth: 2, Index: 0",
Assert.assertEquals("Id: /0/0/1/0/2/0\nDepth: 2, Index: 0",
tree.getItem(2).getText());
Assert.assertEquals("Id: /0/0/1/0/2/1, Depth: 2, Index: 1",
Assert.assertEquals("Id: /0/0/1/0/2/1\nDepth: 2, Index: 1",
tree.getItem(3).getText());

assertNoErrorNotifications();
}

@Test
public void tree_html_caption_and_expander_position() {
// Set row height big enough to show whole content.
selectMenuPath("Component", "Row Height",
String.valueOf(TreeBasicFeatures.ROW_HEIGHTS[1]));

selectMenuPath("Component", "Captions", "HTML caption");
TreeElement tree = $(TreeElement.class).first();
Assert.assertEquals("Id: /0/0\nDepth: 0\nIndex: 0",
tree.getItem(0).getText());

Assert.assertEquals("Expander element not aligned to top",
tree.getExpandElement(0).getLocation().getY(),
tree.getItem(0).getLocation().getY());

assertNoErrorNotifications();
}

@Test
public void tree_html_caption_text_mode() {
// Set row height big enough to show whole content.
selectMenuPath("Component", "Captions", "HTML caption");
selectMenuPath("Component", "ContentMode", "TEXT");

TreeElement tree = $(TreeElement.class).first();
Assert.assertEquals("Id: /0/0<br/>Depth: 0<br/>Index: 0",
tree.getItem(0).getText());

assertNoErrorNotifications();
}

@Test
public void tree_item_click() {
selectMenuPath("Component", "Item Click Listener");
@@ -203,4 +240,16 @@ public class TreeBasicFeaturesTest extends MultiBrowserTest {
Assert.assertFalse("First row was not deselected",
wrap.getRow(0).isSelected());
}

@Test
public void tree_row_heigth() {
TreeElement tree = $(TreeElement.class).first();
TreeGridElement wrap = tree.wrap(TreeGridElement.class);
Arrays.stream(TreeBasicFeatures.ROW_HEIGHTS).boxed()
.map(String::valueOf).forEach(height -> {
selectMenuPath("Component", "Row Height", height);
Assert.assertTrue(wrap.getCell(0, 0).getAttribute("style")
.contains("height: " + height + "px;"));
});
}
}

Loading…
Cancel
Save