Browse Source

Resize should work within Grid details row (#9799)

Fixes #7341
tags/7.7.11
Ansku 6 years ago
parent
commit
6e30d6b7d6

+ 69
- 4
client/src/main/java/com/vaadin/client/connectors/GridConnector.java View File

@@ -52,6 +52,8 @@ import com.vaadin.client.ui.AbstractComponentConnector;
import com.vaadin.client.ui.AbstractHasComponentsConnector;
import com.vaadin.client.ui.ConnectorFocusAndBlurHandler;
import com.vaadin.client.ui.SimpleManagedLayout;
import com.vaadin.client.ui.layout.ElementResizeEvent;
import com.vaadin.client.ui.layout.ElementResizeListener;
import com.vaadin.client.widget.escalator.events.RowHeightChangedEvent;
import com.vaadin.client.widget.escalator.events.RowHeightChangedHandler;
import com.vaadin.client.widget.grid.CellReference;
@@ -496,17 +498,67 @@ public class GridConnector extends AbstractHasComponentsConnector

private final Map<String, ComponentConnector> idToDetailsMap = new HashMap<String, ComponentConnector>();
private final Map<String, Integer> idToRowIndex = new HashMap<String, Integer>();
private final Map<Element, ScheduledCommand> elementToResizeCommand = new HashMap<Element, Scheduler.ScheduledCommand>();
private final ElementResizeListener detailsRowResizeListener = new ElementResizeListener() {

@Override
public void onElementResize(ElementResizeEvent e) {
if (elementToResizeCommand.containsKey(e.getElement())) {
Scheduler.get().scheduleFinally(
elementToResizeCommand.get(e.getElement()));
}
}
};

/* calculated when the first details row is opened */
private Double spacerCellBorderHeights = null;

@Override
public Widget getDetails(int rowIndex) {
String id = getId(rowIndex);
if (id == null) {
if (id == null || !hasDetailsOpen(rowIndex)) {
return null;
}
ComponentConnector componentConnector = idToDetailsMap.get(id);
idToRowIndex.put(id, rowIndex);

return componentConnector.getWidget();
Widget widget = componentConnector.getWidget();
getLayoutManager().addElementResizeListener(widget.getElement(),
detailsRowResizeListener);
elementToResizeCommand.put(widget.getElement(),
createResizeCommand(rowIndex, widget.getElement()));

return widget;
}

private ScheduledCommand createResizeCommand(final int rowIndex,
final Element element) {
return new ScheduledCommand() {

@Override
public void execute() {
// It should not be possible to get here without calculating
// the spacerCellBorderHeights or without having the details
// row open, nor for this command to be triggered while
// layout is running, but it's safer to check anyway.
if (spacerCellBorderHeights != null
&& !getLayoutManager().isLayoutRunning()
&& hasDetailsOpen(rowIndex)) {
double height = getLayoutManager().getOuterHeightDouble(
element) + spacerCellBorderHeights;
getWidget().setDetailsHeight(rowIndex, height);
}
}
};
}

private boolean hasDetailsOpen(int rowIndex) {
JsonObject row = getWidget().getDataSource().getRow(rowIndex);
if (row.hasKey(GridState.JSONKEY_DETAILS_VISIBLE)) {
String id = row.getString(GridState.JSONKEY_DETAILS_VISIBLE);
return id != null && !id.isEmpty();
}
return false;
}

@Override
@@ -519,8 +571,16 @@ public class GridConnector extends AbstractHasComponentsConnector
getLayoutManager().setNeedsMeasureRecursively(componentConnector);
getLayoutManager().layoutNow();

return getLayoutManager().getOuterHeightDouble(
componentConnector.getWidget().getElement());
Element element = componentConnector.getWidget().getElement();
if (spacerCellBorderHeights == null) {
// If theme is changed, new details generator is created from
// scratch, so this value doesn't need to be updated elsewhere.
spacerCellBorderHeights = WidgetUtil
.getBorderTopAndBottomThickness(
element.getParentElement());
}

return getLayoutManager().getOuterHeightDouble(element);
}

/**
@@ -568,6 +628,11 @@ public class GridConnector extends AbstractHasComponentsConnector
}

for (String id : removedDetails) {
Element element = idToDetailsMap.get(id).getWidget()
.getElement();
elementToResizeCommand.remove(element);
getLayoutManager().removeElementResizeListener(element,
detailsRowResizeListener);
idToDetailsMap.remove(id);
idToRowIndex.remove(id);
}

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

@@ -9117,6 +9117,19 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>,
return visibleDetails.contains(Integer.valueOf(rowIndex));
}

/**
* Update details row height.
*
* @since
* @param rowIndex
* the index of the row for which to update details height
* @param height
* new height of the details row
*/
public void setDetailsHeight(int rowIndex, double height) {
escalator.getBody().setSpacer(rowIndex, height);
}

/**
* Requests that the column widths should be recalculated.
* <p>

+ 135
- 0
uitest/src/main/java/com/vaadin/tests/components/grid/GridLayoutDetailsRowResize.java View File

@@ -0,0 +1,135 @@
/*
* Copyright 2000-2017 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.tests.components.grid;

import com.vaadin.event.ItemClickEvent;
import com.vaadin.event.ItemClickEvent.ItemClickListener;
import com.vaadin.server.VaadinRequest;
import com.vaadin.tests.components.AbstractTestUI;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Component;
import com.vaadin.ui.Grid;
import com.vaadin.ui.Grid.DetailsGenerator;
import com.vaadin.ui.Grid.RowReference;
import com.vaadin.ui.Label;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.themes.Reindeer;
import com.vaadin.ui.themes.ValoTheme;

/**
* Tests that details row resizes along with the contents properly.
*
* @author Vaadin Ltd
*/
public class GridLayoutDetailsRowResize extends AbstractTestUI {

@Override
protected void setup(VaadinRequest request) {
final Grid grid = new Grid();
grid.setSizeFull();
grid.addColumn("name", String.class);
grid.addColumn("born", Integer.class);

grid.addRow("Nicolaus Copernicus", 1543);
grid.addRow("Galileo Galilei", 1564);
grid.addRow("Johannes Kepler", 1571);

addComponent(grid);

grid.setDetailsGenerator(new DetailsGenerator() {
@Override
public Component getDetails(final RowReference rowReference) {
final VerticalLayout detailsLayout = new VerticalLayout();
detailsLayout.setId("details");
detailsLayout.setSizeFull();
detailsLayout.setHeightUndefined();

final Label lbl1 = new Label("test1");
lbl1.setId("lbl1");
lbl1.setWidth("200px");
detailsLayout.addComponent(lbl1);

final Label lbl2 = new Label("test2");
lbl2.setId("lbl2");
detailsLayout.addComponent(lbl2);

final Label lbl3 = new Label("test3");
lbl3.setId("lbl3");
detailsLayout.addComponent(lbl3);

final Label lbl4 = new Label("test4");
lbl4.setId("lbl4");
lbl4.setVisible(false);
detailsLayout.addComponent(lbl4);

final Button button = new Button("Toggle visibility",
new Button.ClickListener() {

@Override
public void buttonClick(ClickEvent event) {
lbl4.setVisible(!lbl4.isVisible());
}
});
button.setId("btn");
detailsLayout.addComponent(button);

return detailsLayout;
}
});

grid.addItemClickListener(new ItemClickListener() {
@Override
public void itemClick(final ItemClickEvent event) {
final Object itemId = event.getItemId();
grid.setDetailsVisible(itemId, !grid.isDetailsVisible(itemId));
}
});

addComponent(new Button("Toggle theme", new Button.ClickListener() {

@Override
public void buttonClick(ClickEvent event) {
if (ValoTheme.THEME_NAME.equals(getUI().getTheme())) {
getUI().setTheme(Reindeer.THEME_NAME);
} else {
getUI().setTheme(ValoTheme.THEME_NAME);
}
}
}));

addComponent(new Button("Open details", new Button.ClickListener() {

@Override
public void buttonClick(ClickEvent event) {
for (Object itemId : grid.getContainerDataSource()
.getItemIds()) {
grid.setDetailsVisible(itemId, true);
}
}
}));
}

@Override
protected String getTestDescription() {
return "Detail row should be correctly resized when its contents change.";
}

@Override
protected Integer getTicketNumber() {
return 7341;
}
}

+ 160
- 0
uitest/src/test/java/com/vaadin/tests/components/grid/GridLayoutDetailsRowResizeTest.java View File

@@ -0,0 +1,160 @@
/*
* Copyright 2000-2017 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.tests.components.grid;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.number.IsCloseTo.closeTo;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;

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

import com.vaadin.testbench.elements.ButtonElement;
import com.vaadin.testbench.elements.GridElement;
import com.vaadin.testbench.elements.LabelElement;
import com.vaadin.testbench.elements.VerticalLayoutElement;
import com.vaadin.testbench.parallel.TestCategory;
import com.vaadin.tests.tb3.MultiBrowserTest;

/**
* Tests that details row resizes along with the contents properly.
*
* @author Vaadin Ltd
*/
@TestCategory("grid")
public class GridLayoutDetailsRowResizeTest extends MultiBrowserTest {

@Test
public void testLabelHeights() {
openTestURL();
waitForElementPresent(By.className("v-grid"));

GridElement grid = $(GridElement.class).first();

grid.getCell(2, 0).click();
waitForElementPresent(By.id("lbl2"));

VerticalLayoutElement layout = $(VerticalLayoutElement.class)
.id("details");
int layoutHeight = layout.getSize().height;

ButtonElement button = $(ButtonElement.class).id("btn");
int buttonHeight = button.getSize().height;

// height should be divided equally
double expectedLabelHeight = (layoutHeight - buttonHeight) / 3;
assertLabelHeight("lbl1", expectedLabelHeight);
assertLabelHeight("lbl2", expectedLabelHeight);
assertLabelHeight("lbl3", expectedLabelHeight);

assertDetailsRowHeight(layoutHeight);

// ensure fourth label isn't present yet
assertElementNotPresent(By.id("lbl4"));

button.click();
waitForElementPresent(By.id("lbl4"));

// get layout height after the new label has been added
layoutHeight = layout.getSize().height;

expectedLabelHeight = (layoutHeight - buttonHeight) / 4;
assertLabelHeight("lbl1", expectedLabelHeight);
assertLabelHeight("lbl2", expectedLabelHeight);
assertLabelHeight("lbl3", expectedLabelHeight);
assertLabelHeight("lbl4", expectedLabelHeight);

assertDetailsRowHeight(layoutHeight);
}

@Test
public void testMultipleDetailsRows() {
openTestURL();
waitForElementPresent(By.className("v-grid"));

ButtonElement detailsButton = $(ButtonElement.class)
.caption("Open details").first();

detailsButton.click();
waitForElementPresent(By.id("lbl2"));

List<ButtonElement> buttons = $(ButtonElement.class)
.caption("Toggle visibility").all();
assertThat("Unexpected amount of details rows.", buttons.size(), is(3));

Map<ButtonElement, Integer> positions = new LinkedHashMap<ButtonElement, Integer>();
Map<Integer, ButtonElement> ordered = new TreeMap<Integer, ButtonElement>();
for (ButtonElement button : buttons) {
positions.put(button, button.getLocation().getY());
ordered.put(button.getLocation().getY(), button);
}
int labelHeight = 0;
for (LabelElement label : $(LabelElement.class).all()) {
if ("test1".equals(label.getText())) {
labelHeight = label.getSize().height;
}
}

// toggle the contents
for (ButtonElement button : buttons) {
button.click();
}

int i = 0;
for (Entry<Integer, ButtonElement> entry : ordered.entrySet()) {
++i;
ButtonElement button = entry.getValue();
assertThat(
String.format("Unexpected button position: details row %s.",
i),
(double) button.getLocation().getY(),
closeTo(positions.get(button) + (i * labelHeight), 1d));
}

// toggle the contents
for (ButtonElement button : buttons) {
button.click();
}

// assert original positions back
for (ButtonElement button : buttons) {
assertThat(String.format("Unexpected button position."),
(double) button.getLocation().getY(),
closeTo(positions.get(button), 1d));
}
}

private void assertLabelHeight(String id, double expectedHeight) {
// 1px leeway for calculations
assertThat("Unexpected label height.",
(double) $(LabelElement.class).id(id).getSize().height,
closeTo(expectedHeight, 1d));
}

private void assertDetailsRowHeight(int layoutHeight) {
// check that details row height matches layout height (1px leeway)
WebElement detailsRow = findElement(By.className("v-grid-spacer"));
assertThat("Unexpected details row height", (double) layoutHeight,
closeTo(detailsRow.getSize().height, 1d));
}
}

Loading…
Cancel
Save