diff options
author | Anna Koskinen <anna@vaadin.com> | 2016-06-01 11:56:34 +0300 |
---|---|---|
committer | Johannes Dahlström <johannesd@vaadin.com> | 2016-06-29 12:43:16 +0000 |
commit | edad7348bb8eba807225bfa72d4b0a4342426c71 (patch) | |
tree | 4eefaf917c8ef6ab7c7c250597de32b94c8a4fe5 | |
parent | 75b282c319b2783d0ea2737727081c6923342ac0 (diff) | |
download | vaadin-framework-edad7348bb8eba807225bfa72d4b0a4342426c71.tar.gz vaadin-framework-edad7348bb8eba807225bfa72d4b0a4342426c71.zip |
Updates to Grid's height handling (#19690).
- new height more for undefined height that works like in Table and
resizes the grid when details row opens or closes
Change-Id: I2dc817140308093865be30de72edcd6494e4a44b
5 files changed, 436 insertions, 6 deletions
diff --git a/client/src/main/java/com/vaadin/client/widgets/Escalator.java b/client/src/main/java/com/vaadin/client/widgets/Escalator.java index 29b7eb6d53..25e83592d7 100644 --- a/client/src/main/java/com/vaadin/client/widgets/Escalator.java +++ b/client/src/main/java/com/vaadin/client/widgets/Escalator.java @@ -1148,6 +1148,9 @@ public class Escalator extends Widget implements RequiresResize, assertArgumentsAreValidAndWithinRange(index, numberOfRows); rows -= numberOfRows; + if (heightMode == HeightMode.UNDEFINED) { + heightByRows = rows; + } if (!isAttached()) { return; @@ -1271,6 +1274,9 @@ public class Escalator extends Widget implements RequiresResize, } rows += numberOfRows; + if (heightMode == HeightMode.UNDEFINED) { + heightByRows = rows; + } /* * only add items in the DOM if the widget itself is attached to the @@ -5826,7 +5832,13 @@ public class Escalator extends Widget implements RequiresResize, if (height != null && !height.isEmpty()) { heightByCss = height; } else { - heightByCss = DEFAULT_HEIGHT; + if (getHeightMode() == HeightMode.UNDEFINED) { + heightByRows = body.getRowCount(); + applyHeightByRows(); + return; + } else { + heightByCss = DEFAULT_HEIGHT; + } } if (getHeightMode() == HeightMode.CSS) { @@ -5840,7 +5852,16 @@ public class Escalator extends Widget implements RequiresResize, if (height != null && !height.isEmpty()) { super.setHeight(height); } else { - super.setHeight(DEFAULT_HEIGHT); + if (getHeightMode() == HeightMode.UNDEFINED) { + int newHeightByRows = body.getRowCount(); + if (heightByRows != newHeightByRows) { + heightByRows = newHeightByRows; + applyHeightByRows(); + } + return; + } else { + super.setHeight(DEFAULT_HEIGHT); + } } recalculateElementSizes(); @@ -6318,7 +6339,7 @@ public class Escalator extends Widget implements RequiresResize, * define its height that way. */ private void applyHeightByRows() { - if (heightMode != HeightMode.ROW) { + if (heightMode != HeightMode.ROW && heightMode != HeightMode.UNDEFINED) { return; } @@ -6327,9 +6348,13 @@ public class Escalator extends Widget implements RequiresResize, double bodyHeight = body.getDefaultRowHeight() * heightByRows; double scrollbar = horizontalScrollbar.showsScrollHandle() ? horizontalScrollbar .getScrollbarThickness() : 0; + double spacerHeight = 0; // ignored if HeightMode.ROW + if (heightMode == HeightMode.UNDEFINED) { + spacerHeight = body.spacerContainer.getSpacerHeightsSum(); + } - double totalHeight = headerHeight + bodyHeight + scrollbar - + footerHeight; + double totalHeight = headerHeight + bodyHeight + spacerHeight + + scrollbar + footerHeight; setHeightInternal(totalHeight + "px"); } @@ -6370,6 +6395,9 @@ public class Escalator extends Widget implements RequiresResize, case ROW: setHeightByRows(heightByRows); break; + case UNDEFINED: + setHeightByRows(body.getRowCount()); + break; default: throw new IllegalStateException("Unimplemented feature " + "- unknown HeightMode: " + this.heightMode); diff --git a/client/src/main/java/com/vaadin/client/widgets/Grid.java b/client/src/main/java/com/vaadin/client/widgets/Grid.java index 93aeac0d68..7665111416 100644 --- a/client/src/main/java/com/vaadin/client/widgets/Grid.java +++ b/client/src/main/java/com/vaadin/client/widgets/Grid.java @@ -3600,6 +3600,9 @@ public class Grid<T> extends ResizeComposite implements } escalator.getBody().setSpacer(rowIndex, spacerHeight); + if (getHeightMode() == HeightMode.UNDEFINED) { + setHeightByRows(getEscalator().getBody().getRowCount()); + } } @Override @@ -3628,6 +3631,11 @@ public class Grid<T> extends ResizeComposite implements setParent(detailsWidget, null); spacerElement.removeAllChildren(); + if (getHeightMode() == HeightMode.UNDEFINED) { + // update spacer height + escalator.getBody().setSpacer(spacer.getRow(), 0); + setHeightByRows(getEscalator().getBody().getRowCount()); + } } } diff --git a/shared/src/main/java/com/vaadin/shared/ui/grid/HeightMode.java b/shared/src/main/java/com/vaadin/shared/ui/grid/HeightMode.java index 4cd19a01b1..7fc992566f 100644 --- a/shared/src/main/java/com/vaadin/shared/ui/grid/HeightMode.java +++ b/shared/src/main/java/com/vaadin/shared/ui/grid/HeightMode.java @@ -38,5 +38,11 @@ public enum HeightMode { * The height of the Component or Widget in question is defined by a number * of rows. */ - ROW; + ROW, + + /** + * The height of the Component or Widget in question is defined by its + * contents. + */ + UNDEFINED; } diff --git a/uitest/src/main/java/com/vaadin/tests/components/grid/GridHeight.java b/uitest/src/main/java/com/vaadin/tests/components/grid/GridHeight.java new file mode 100644 index 0000000000..c22bc56103 --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/grid/GridHeight.java @@ -0,0 +1,189 @@ +/* + * 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.tests.components.grid; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import com.vaadin.data.Property.ValueChangeEvent; +import com.vaadin.data.Property.ValueChangeListener; +import com.vaadin.event.ItemClickEvent; +import com.vaadin.event.ItemClickEvent.ItemClickListener; +import com.vaadin.server.VaadinRequest; +import com.vaadin.shared.ui.grid.HeightMode; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Alignment; +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.HorizontalLayout; +import com.vaadin.ui.Label; +import com.vaadin.ui.OptionGroup; +import com.vaadin.ui.VerticalLayout; + +/** + * Tests that Grid gets correct height based on height mode, and resizes + * properly with details row if height is undefined. + * + * @author Vaadin Ltd + */ +public class GridHeight extends AbstractTestUI { + + static final String FULL = "Full"; + static final String UNDEFINED = "Undefined"; + static final String PX100 = "100px"; + static final Integer ROW3 = 3; + + static final Object[] gridHeights = { FULL, UNDEFINED, ROW3 }; + static final String[] gridWidths = { FULL, UNDEFINED }; + static final String[] detailsRowHeights = { FULL, UNDEFINED, PX100 }; + + private Grid grid; + private Map<Object, VerticalLayout> detailsLayouts = new HashMap<Object, VerticalLayout>(); + private OptionGroup detailsHeightSelector; + + @Override + protected void setup(VaadinRequest request) { + + grid = new Grid(); + grid.addColumn("name", String.class); + grid.addColumn("born", Integer.class); + + grid.addRow("Nicolaus Copernicus", 1543); + grid.addRow("Galileo Galilei", 1564); + for (int i = 0; i < 1; ++i) { + grid.addRow("Johannes Kepler", 1571); + } + + grid.setDetailsGenerator(new DetailsGenerator() { + @Override + public Component getDetails(final RowReference rowReference) { + if (!detailsLayouts.containsKey(rowReference.getItemId())) { + createDetailsLayout(rowReference.getItemId()); + } + return detailsLayouts.get(rowReference.getItemId()); + } + }); + + grid.addItemClickListener(new ItemClickListener() { + @Override + public void itemClick(final ItemClickEvent event) { + final Object itemId = event.getItemId(); + grid.setDetailsVisible(itemId, !grid.isDetailsVisible(itemId)); + } + }); + + addComponent(createOptionLayout()); + addComponent(grid); + } + + private void createDetailsLayout(Object itemId) { + VerticalLayout detailsLayout = new VerticalLayout(); + setDetailsHeight(detailsLayout, detailsHeightSelector.getValue()); + detailsLayout.setWidth("100%"); + + Label lbl1 = new Label("details row"); + lbl1.setId("lbl1"); + lbl1.setSizeUndefined(); + detailsLayout.addComponent(lbl1); + detailsLayout.setComponentAlignment(lbl1, Alignment.MIDDLE_CENTER); + + detailsLayouts.put(itemId, detailsLayout); + } + + private Component createOptionLayout() { + HorizontalLayout optionLayout = new HorizontalLayout(); + OptionGroup gridHeightSelector = new OptionGroup("Grid height", + Arrays.<Object> asList(gridHeights)); + gridHeightSelector.setId("gridHeightSelector"); + gridHeightSelector.setItemCaption(ROW3, ROW3 + " rows"); + gridHeightSelector.addValueChangeListener(new ValueChangeListener() { + @Override + public void valueChange(ValueChangeEvent event) { + Object value = event.getProperty().getValue(); + if (UNDEFINED.equals(value)) { + grid.setHeightUndefined(); + grid.setHeightMode(HeightMode.UNDEFINED); + } else if (FULL.equals(value)) { + grid.setHeight("100%"); + grid.setHeightMode(HeightMode.CSS); + } else if (ROW3.equals(value)) { + grid.setHeightByRows(ROW3); + grid.setHeightMode(HeightMode.ROW); + } + } + }); + gridHeightSelector.setValue(UNDEFINED); + optionLayout.addComponent(gridHeightSelector); + + OptionGroup gridWidthSelector = new OptionGroup("Grid width", + Arrays.asList(gridWidths)); + gridWidthSelector.setId("gridWidthSelector"); + gridWidthSelector.addValueChangeListener(new ValueChangeListener() { + @Override + public void valueChange(ValueChangeEvent event) { + Object value = event.getProperty().getValue(); + if (UNDEFINED.equals(value)) { + grid.setWidthUndefined(); + } else if (FULL.equals(value)) { + grid.setWidth("100%"); + } + } + }); + gridWidthSelector.setValue(UNDEFINED); + optionLayout.addComponent(gridWidthSelector); + + detailsHeightSelector = new OptionGroup("Details row height", + Arrays.asList(detailsRowHeights)); + detailsHeightSelector.setId("detailsHeightSelector"); + detailsHeightSelector.addValueChangeListener(new ValueChangeListener() { + @Override + public void valueChange(ValueChangeEvent event) { + Object value = event.getProperty().getValue(); + for (VerticalLayout detailsLayout : detailsLayouts.values()) { + setDetailsHeight(detailsLayout, value); + } + } + }); + detailsHeightSelector.setValue(PX100); + optionLayout.addComponent(detailsHeightSelector); + return optionLayout; + } + + private void setDetailsHeight(VerticalLayout detailsLayout, Object value) { + if (UNDEFINED.equals(value)) { + detailsLayout.setHeightUndefined(); + } else if (FULL.equals(value)) { + detailsLayout.setHeight("100%"); + } else if (PX100.equals(value)) { + detailsLayout.setHeight(PX100); + } + } + + @Override + protected String getTestDescription() { + return "Grid with undefined height should display all rows and resize when details row is opened." + + "<br>Grid with full height is always 400px high regardless or details row." + + "<br>Grid with row height should always be the height of those rows regardless of details row."; + } + + @Override + protected Integer getTicketNumber() { + return 19690; + } +} diff --git a/uitest/src/test/java/com/vaadin/tests/components/grid/GridHeightTest.java b/uitest/src/test/java/com/vaadin/tests/components/grid/GridHeightTest.java new file mode 100644 index 0000000000..14ad0224d6 --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/grid/GridHeightTest.java @@ -0,0 +1,199 @@ +/* + * 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.tests.components.grid; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.number.IsCloseTo.closeTo; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import org.junit.Assert; +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.remote.DesiredCapabilities; + +import com.vaadin.testbench.elements.GridElement; +import com.vaadin.testbench.elements.OptionGroupElement; +import com.vaadin.testbench.parallel.BrowserUtil; +import com.vaadin.testbench.parallel.TestCategory; +import com.vaadin.tests.tb3.MultiBrowserTest; + +/** + * Tests that Grid gets correct height based on height mode, and resizes + * properly with details row if height is undefined. + * + * @author Vaadin Ltd + */ +@TestCategory("grid") +public class GridHeightTest extends MultiBrowserTest { + + @Override + public void setup() throws Exception { + super.setup(); + openTestURL(); + waitForElementPresent(By.className("v-grid")); + } + + @Test + public void testGridHeightAndResizingUndefined() + throws InterruptedException { + assertNoErrors(testGridHeightAndResizing(GridHeight.UNDEFINED)); + } + + @Test + public void testGridHeightAndResizingRow() throws InterruptedException { + if (isIE8orIE9()) { + /* + * with IE8 and IE9 and this height mode grid resizes when it + * shouldn't and doesn't resize when it should, pre-existing problem + * that isn't within the scope of this ticket + */ + return; + } + assertNoErrors(testGridHeightAndResizing(GridHeight.ROW3)); + } + + @Test + public void testGridHeightAndResizingFull() throws InterruptedException { + assertNoErrors(testGridHeightAndResizing(GridHeight.FULL)); + } + + private Map<AssertionError, Object[]> testGridHeightAndResizing( + Object gridHeight) throws InterruptedException { + Map<AssertionError, Object[]> errors = new HashMap<AssertionError, Object[]>(); + String caption; + if (GridHeight.ROW3.equals(gridHeight)) { + caption = gridHeight + " rows"; + } else { + caption = (String) gridHeight; + } + $(OptionGroupElement.class).id("gridHeightSelector").selectByText( + caption); + for (String gridWidth : GridHeight.gridWidths) { + $(OptionGroupElement.class).id("gridWidthSelector").selectByText( + gridWidth); + for (String detailsRowHeight : GridHeight.detailsRowHeights) { + $(OptionGroupElement.class).id("detailsHeightSelector") + .selectByText(detailsRowHeight); + sleep(500); + + GridElement grid = $(GridElement.class).first(); + int initialHeight = grid.getSize().getHeight(); + try { + // check default height + assertGridHeight(getExpectedInitialHeight(gridHeight), + initialHeight); + } catch (AssertionError e) { + errors.put(e, new Object[] { gridHeight, gridWidth, + detailsRowHeight, "initial" }); + } + + grid.getRow(2).click(5, 5); + waitForElementPresent(By.id("lbl1")); + + int openHeight = grid.getSize().getHeight(); + try { + // check height with details row opened + assertGridHeight( + getExpectedOpenedHeight(gridHeight, + detailsRowHeight), openHeight); + } catch (AssertionError e) { + errors.put(e, new Object[] { gridHeight, gridWidth, + detailsRowHeight, "opened" }); + } + + grid.getRow(2).click(5, 5); + waitForElementNotPresent(By.id("lbl1")); + + int afterHeight = grid.getSize().getHeight(); + try { + // check height with details row closed again + assertThat("Unexpected Grid Height", afterHeight, + is(initialHeight)); + } catch (AssertionError e) { + errors.put(e, new Object[] { gridHeight, gridWidth, + detailsRowHeight, "closed" }); + } + } + } + return errors; + } + + private void assertNoErrors(Map<AssertionError, Object[]> errors) { + if (!errors.isEmpty()) { + StringBuilder sb = new StringBuilder("Exceptions: "); + for (Entry<AssertionError, Object[]> entry : errors.entrySet()) { + sb.append("\n"); + for (Object value : entry.getValue()) { + sb.append(value); + sb.append(" - "); + } + sb.append(entry.getKey().getMessage()); + } + Assert.fail(sb.toString()); + } + } + + private int getExpectedInitialHeight(Object gridHeight) { + int result = 0; + if (GridHeight.UNDEFINED.equals(gridHeight) + || GridHeight.ROW3.equals(gridHeight)) { + result = 81; + } else if (GridHeight.FULL.equals(gridHeight)) { + // pre-existing issue + result = 400; + } + return result; + } + + private int getExpectedOpenedHeight(Object gridHeight, + Object detailsRowHeight) { + int result = 0; + if (GridHeight.UNDEFINED.equals(gridHeight)) { + if (GridHeight.PX100.equals(detailsRowHeight)) { + result = 182; + } else if (GridHeight.FULL.equals(detailsRowHeight)) { + if (isIE8orIE9()) { + // pre-existing bug in IE8 & IE9, details row doesn't layout + // itself properly + result = 100; + } else { + result = 131; + } + } else if (GridHeight.UNDEFINED.equals(detailsRowHeight)) { + result = 100; + } + } else if (GridHeight.ROW3.equals(gridHeight) + || GridHeight.FULL.equals(gridHeight)) { + result = getExpectedInitialHeight(gridHeight); + } + return result; + } + + private boolean isIE8orIE9() { + DesiredCapabilities desiredCapabilities = getDesiredCapabilities(); + return BrowserUtil.isIE8(desiredCapabilities) + || BrowserUtil.isIE(desiredCapabilities, 9); + } + + private void assertGridHeight(int expected, int actual) { + assertThat("Unexpected Grid Height", (double) actual, + closeTo(expected, 1)); + } +} |