diff options
author | Anna Koskinen <Ansku@users.noreply.github.com> | 2018-03-21 15:54:26 +0200 |
---|---|---|
committer | Ilia Motornyi <elmot@vaadin.com> | 2018-03-21 15:54:26 +0200 |
commit | 1187cf22f06df587b2a1ec1068be88e2b70c888d (patch) | |
tree | 9712c2700de577ec195e1b159aaf51dd27ee65bc | |
parent | 940a1ef584c82e343474863408771ff091044bdc (diff) | |
download | vaadin-framework-1187cf22f06df587b2a1ec1068be88e2b70c888d.tar.gz vaadin-framework-1187cf22f06df587b2a1ec1068be88e2b70c888d.zip |
Grid editor open (#10674)
5 files changed, 336 insertions, 38 deletions
diff --git a/client/src/main/java/com/vaadin/client/WidgetUtil.java b/client/src/main/java/com/vaadin/client/WidgetUtil.java index 11a5ae363c..b995ef15c0 100644 --- a/client/src/main/java/com/vaadin/client/WidgetUtil.java +++ b/client/src/main/java/com/vaadin/client/WidgetUtil.java @@ -52,6 +52,37 @@ import com.vaadin.shared.util.SharedUtil; public class WidgetUtil { /** + * Simple object to store another object. + * + * @param <T> + * the object type to store + * @since + */ + public static class Reference<T> { + + T reference = null; + + /** + * Gets the current object. + * + * @return the stored object + */ + public T get() { + return reference; + } + + /** + * Sets the current object. + * + * @param reference + * the object to store + */ + public void set(T reference) { + this.reference = reference; + } + } + + /** * Helper method for debugging purposes. * * Stops execution on firefox browsers on a breakpoint. @@ -778,7 +809,7 @@ public class WidgetUtil { com.google.gwt.dom.client.Element el, String p) /*-{ try { - + if (el.currentStyle) { // IE return el.currentStyle[p]; @@ -793,7 +824,7 @@ public class WidgetUtil { } catch (e) { return ""; } - + }-*/; /** @@ -807,7 +838,7 @@ public class WidgetUtil { try { el.focus(); } catch (e) { - + } }-*/; @@ -1158,7 +1189,7 @@ public class WidgetUtil { if ($wnd.document.activeElement) { return $wnd.document.activeElement; } - + return null; }-*/; @@ -1229,11 +1260,11 @@ public class WidgetUtil { /*-{ var top = elem.offsetTop; var height = elem.offsetHeight; - + if (elem.parentNode != elem.offsetParent) { top -= elem.parentNode.offsetTop; } - + var cur = elem.parentNode; while (cur && (cur.nodeType == 1)) { if (top < cur.scrollTop) { @@ -1242,12 +1273,12 @@ public class WidgetUtil { if (top + height > cur.scrollTop + cur.clientHeight) { cur.scrollTop = (top + height) - cur.clientHeight; } - + var offsetTop = cur.offsetTop; if (cur.parentNode != cur.offsetParent) { offsetTop -= cur.parentNode.offsetTop; } - + top += offsetTop - cur.scrollTop; cur = cur.parentNode; } @@ -1696,7 +1727,7 @@ public class WidgetUtil { } var heightWithoutBorder = cloneElement.offsetHeight; parentElement.removeChild(cloneElement); - + return heightWithBorder - heightWithoutBorder; } }-*/; @@ -1873,7 +1904,7 @@ public class WidgetUtil { * ancestors has the style {@code display: none} applied. * * @param element - * the element to test for visibility + * the element to test for visibility * @return {@code true} if the element is displayed, {@code false} otherwise * @since 8.3.2 */ diff --git a/client/src/main/java/com/vaadin/client/connectors/grid/EditorConnector.java b/client/src/main/java/com/vaadin/client/connectors/grid/EditorConnector.java index c8c60c9620..8e2d050d55 100644 --- a/client/src/main/java/com/vaadin/client/connectors/grid/EditorConnector.java +++ b/client/src/main/java/com/vaadin/client/connectors/grid/EditorConnector.java @@ -66,12 +66,7 @@ public class EditorConnector extends AbstractExtensionConnector { public void bind(final int rowIndex) { // call this deferred to avoid issues with editing on init Scheduler.get().scheduleDeferred(() -> { - currentEditedRow = rowIndex; - // might need to wait for available data, - // if data is available, ensureAvailability will immediately trigger the handler anyway, - // so no need for alternative "immediately available" logic - waitingForAvailableData = true; - getParent().getDataSource().ensureAvailability(rowIndex, 1); + getParent().getWidget().editRow(rowIndex); }); } @@ -218,13 +213,6 @@ public class EditorConnector extends AbstractExtensionConnector { protected void extend(ServerConnector target) { Grid<JsonObject> grid = getParent().getWidget(); grid.getEditor().setHandler(new CustomEditorHandler()); - grid.addDataAvailableHandler((event) -> { - Range range = event.getAvailableRows(); - if (waitingForAvailableData && currentEditedRow != null && range.contains(currentEditedRow)) { - getParent().getWidget().editRow(currentEditedRow); - waitingForAvailableData = false; - } - }); } @Override 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 1d24086b3b..5a31ec1f41 100755 --- a/client/src/main/java/com/vaadin/client/widgets/Grid.java +++ b/client/src/main/java/com/vaadin/client/widgets/Grid.java @@ -80,6 +80,7 @@ import com.vaadin.client.BrowserInfo; import com.vaadin.client.DeferredWorker; import com.vaadin.client.Focusable; import com.vaadin.client.WidgetUtil; +import com.vaadin.client.WidgetUtil.Reference; import com.vaadin.client.data.DataChangeHandler; import com.vaadin.client.data.DataSource; import com.vaadin.client.data.DataSource.RowHandle; @@ -1437,7 +1438,6 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, private String styleName = null; private HandlerRegistration hScrollHandler; - private HandlerRegistration vScrollHandler; private final Button saveButton; private final Button cancelButton; @@ -1680,20 +1680,10 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, } state = State.ACTIVATING; - final Escalator escalator = grid.getEscalator(); - if (escalator.getVisibleRowRange().contains(rowIndex)) { - show(rowIndex, columnIndexDOM); - } else { - vScrollHandler = grid.addScrollHandler(event -> { - if (escalator.getVisibleRowRange().contains(rowIndex)) { - show(rowIndex, columnIndexDOM); - vScrollHandler.removeHandler(); - } - }); - grid.scrollToRow(rowIndex, - isBuffered() ? ScrollDestination.MIDDLE - : ScrollDestination.ANY); - } + grid.scrollToRow(rowIndex, + isBuffered() ? ScrollDestination.MIDDLE + : ScrollDestination.ANY, + () -> show(rowIndex, columnIndexDOM)); } /** @@ -7384,6 +7374,45 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, } /** + * Helper method for making sure desired row is visible and it is properly + * rendered. + * + * @param rowIndex + * the row to look for + * @param destination + * the desired scroll destination + * @param callback + * the callback command to execute when row is available + * @since + */ + public void scrollToRow(int rowIndex, ScrollDestination destination, + Runnable callback) { + waitUntilVisible(rowIndex, destination, () -> { + Reference<HandlerRegistration> registration = new Reference<>(); + registration.set(addDataAvailableHandler(event -> { + if (event.getAvailableRows().contains(rowIndex)) { + registration.get().removeHandler(); + callback.run(); + } + })); + }); + } + + /** + * Helper method for making sure desired row is visible and it is properly + * rendered. + * + * @param rowIndex + * the row to look for + * @param whenRendered + * the callback command to execute when row is available + * @since + */ + public void scrollToRow(int rowIndex, Runnable whenRendered) { + scrollToRow(rowIndex, ScrollDestination.ANY, whenRendered); + } + + /** * Scrolls to a certain row using only user-specified parameters. * <p> * If the details for that row are visible, those will be taken into account @@ -7421,6 +7450,43 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, } /** + * Helper method for scrolling and making sure row is visible. + * + * @param rowIndex + * the row index to make visible + * @param destination + * the desired scroll destination + * @param whenVisible + * the callback method to call when row is visible + */ + private void waitUntilVisible(int rowIndex, ScrollDestination destination, + Runnable whenVisible) { + final Escalator escalator = getEscalator(); + if (escalator.getVisibleRowRange().contains(rowIndex)) { + TableRowElement rowElement = escalator.getBody() + .getRowElement(rowIndex); + long bottomBorder = Math.round(WidgetUtil.getBorderBottomThickness( + rowElement.getFirstChildElement()) + 0.5d); + if (rowElement.getAbsoluteTop() >= escalator.getHeader() + .getElement().getAbsoluteBottom() + && rowElement.getAbsoluteBottom() <= escalator.getFooter() + .getElement().getAbsoluteTop() + bottomBorder) { + whenVisible.run(); + return; + } + } + + Reference<HandlerRegistration> registration = new Reference<>(); + registration.set(addScrollHandler(event -> { + if (escalator.getVisibleRowRange().contains(rowIndex)) { + registration.get().removeHandler(); + whenVisible.run(); + } + })); + scrollToRow(rowIndex, destination); + } + + /** * Scrolls to the beginning of the very first row. */ public void scrollToStart() { diff --git a/uitest/src/main/java/com/vaadin/tests/components/grid/GridEditRow.java b/uitest/src/main/java/com/vaadin/tests/components/grid/GridEditRow.java new file mode 100644 index 0000000000..bd24f53380 --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/grid/GridEditRow.java @@ -0,0 +1,97 @@ +package com.vaadin.tests.components.grid; + +import java.util.ArrayList; + +import com.vaadin.annotations.Widgetset; +import com.vaadin.data.provider.ListDataProvider; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Button; +import com.vaadin.ui.Grid; +import com.vaadin.ui.TextField; + +@Widgetset("com.vaadin.DefaultWidgetSet") +public class GridEditRow extends AbstractTestUI { + + private ArrayList<TestBean> items = new ArrayList<>(); + private TestBean bean = null; + private int counter = 0; + + @SuppressWarnings("unchecked") + @Override + protected void setup(VaadinRequest request) { + + Grid<TestBean> grid = new Grid<TestBean>(TestBean.class); + grid.setDataProvider(new ListDataProvider<>(items)); + grid.setWidth("100%"); + grid.setHeight("400px"); + grid.getEditor().setEnabled(true); + grid.getColumn("name").setEditorComponent(new TextField()); + grid.getColumn("value").setEditorComponent(new TextField()); + + getLayout().addComponent(new Button("Add", event -> { + bean = new TestBean(); + items.add(bean); + grid.getDataProvider().refreshAll(); + })); + getLayout().addComponent(new Button("Add, Select & Edit", event -> { + bean = new TestBean(); + items.add(bean); + grid.getDataProvider().refreshAll(); + grid.select(bean); + int row = ((ArrayList<TestBean>) ((ListDataProvider<TestBean>) grid + .getDataProvider()).getItems()).indexOf(bean); + grid.getEditor().editRow(row); + })); + getLayout().addComponent(new Button("Edit", event -> { + int row = ((ArrayList<TestBean>) ((ListDataProvider<TestBean>) grid + .getDataProvider()).getItems()).indexOf(bean); + grid.getEditor().editRow(row); + })); + getLayout().addComponent(new Button("Select & Edit", event -> { + grid.select(bean); + int row = ((ArrayList<TestBean>) ((ListDataProvider<TestBean>) grid + .getDataProvider()).getItems()).indexOf(bean); + grid.getEditor().editRow(row); + })); + + getLayout().addComponent(grid); + } + + @Override + protected Integer getTicketNumber() { + return 10558; + } + + @Override + protected String getTestDescription() { + return "Calling editRow shouldn't cause any other rows to be emptied."; + } + + public class TestBean { + private String name = "name" + counter; + private String value = "value" + counter; + + public TestBean() { + ++counter; + } + + public String getName() { + return name; + } + + public void setName(String inName) { + name = inName; + } + + public String getValue() { + return value; + } + + public void setValue(String inValue) { + value = inValue; + } + + } + +} diff --git a/uitest/src/test/java/com/vaadin/tests/components/grid/GridEditRowTest.java b/uitest/src/test/java/com/vaadin/tests/components/grid/GridEditRowTest.java new file mode 100644 index 0000000000..d84a09782b --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/grid/GridEditRowTest.java @@ -0,0 +1,116 @@ +/* + * 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.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.greaterThan; + +import org.junit.Test; + +import com.vaadin.testbench.By; +import com.vaadin.testbench.elements.ButtonElement; +import com.vaadin.testbench.elements.GridElement; +import com.vaadin.testbench.elements.GridElement.GridEditorElement; +import com.vaadin.testbench.parallel.TestCategory; +import com.vaadin.tests.tb3.MultiBrowserTest; + +/** + * Tests for ensuring that the furthest away visible rows don't get emptied when + * editRow is called, and that the editor doesn't open beyond the lower border + * of the Grid. + * + */ +@TestCategory("grid") +public class GridEditRowTest extends MultiBrowserTest { + + private GridElement grid; + private ButtonElement addButton; + private ButtonElement editButton; + + @Override + public void setup() throws Exception { + super.setup(); + openTestURL(); + grid = $(GridElement.class).first(); + addButton = $(ButtonElement.class).caption("Add").first(); + editButton = $(ButtonElement.class).caption("Edit").first(); + } + + public void addRows(int count) { + for (int i = 0; i < count; ++i) { + addButton.click(); + } + } + + public void editLastRow() { + editButton.click(); + } + + private void assertRowContents(int rowIndex) { + assertThat(grid.getCell(rowIndex, 0).getText(), is("name" + rowIndex)); + } + + private void assertEditorWithinGrid() { + GridEditorElement editor = grid.getEditor(); + assertThat(editor.getLocation().y + editor.getSize().height, + not(greaterThan(grid.getLocation().y + grid.getSize().height))); + } + + @Test + public void testEditWhenAllRowsVisible() { + addRows(7); + + assertRowContents(0); + + editLastRow(); + + assertRowContents(0); + + waitForElementVisible(By.className("v-grid-editor")); + + assertEditorWithinGrid(); + } + + @Test + public void testEditWhenSomeRowsNotVisible() { + addRows(11); + + assertRowContents(3); + + editLastRow(); + + waitForElementVisible(By.className("v-grid-editor")); + + assertRowContents(3); + assertEditorWithinGrid(); + } + + @Test + public void testEditWhenSomeRowsOutsideOfCache() { + addRows(100); + + assertRowContents(91); + + editLastRow(); + + waitForElementVisible(By.className("v-grid-editor")); + + assertRowContents(91); + assertEditorWithinGrid(); + } +} |