case START:
// target row at the top of the viewport, include buffer
// row if there is room for one
- logicalTargetIndex = Math
+ newTopRowLogicalIndex = Math
.max(firstVisibleIndexIfScrollingUp - 1, 0);
- newTopRowLogicalIndex = logicalTargetIndex;
+ if (getVisibleRowRange().contains(newTopRowLogicalIndex)) {
+ logicalTargetIndex = oldTopRowLogicalIndex
+ + visualRangeLength;
+ } else {
+ logicalTargetIndex = newTopRowLogicalIndex;
+ }
break;
default:
- throw new IllegalArgumentException(
- "Internal: Unsupported ScrollDestination: "
- + destination.name());
+ String msg = "Internal: Unsupported ScrollDestination: ";
+ throw new IllegalArgumentException(msg + destination.name());
}
// adjust visual range if necessary
boolean rowsWereMoved = newTopRowLogicalIndex != oldTopRowLogicalIndex;
// update scroll position if necessary
- double scrollTop = calculateScrollPositionForScrollToRowSpacerOrBoth(
- targetRowIndex, destination, padding, scrollType);
- if (scrollTop != getScrollTop()) {
- setScrollTop(scrollTop);
- setBodyScrollPosition(tBodyScrollLeft, scrollTop);
- }
+ adjustScrollPositionForScrollToRowSpacerOrBoth(targetRowIndex,
+ destination, padding, scrollType);
if (rowsWereMoved) {
fireRowVisibilityChangeEvent();
}
/**
- * Calculates scroll position for
+ * Adjusts scroll position for
* {@link #scrollToRowSpacerOrBoth(int, ScrollDestination, double, boolean, boolean)},
* reuse at your own peril.
*
* @param destination
* @param padding
* @param scrollType
- * @return expected scroll position
*/
- private double calculateScrollPositionForScrollToRowSpacerOrBoth(
+ private void adjustScrollPositionForScrollToRowSpacerOrBoth(
int targetRowIndex, ScrollDestination destination,
double padding, ScrollType scrollType) {
/*
- sectionHeight + padding;
// ensure that we don't overshoot beyond bottom
scrollTop = Math.min(scrollTop,
- getRowTop(getRowCount() - 1) + getDefaultRowHeight()
- + spacerContainer
- .getSpacerHeight(getRowCount() - 1)
- - sectionHeight);
+ getRowTop(getRowCount()) - sectionHeight);
// if padding is set we want to overshoot or undershoot,
// otherwise make sure the top of the row or spacer is
// in view
case END:
if (ScrollType.ROW.equals(scrollType)
&& rowTop + getDefaultRowHeight()
- + padding > getScrollTop() + sectionHeight) {
- // within visual range but end of row below the viewport
- // or not enough padding, shift a little
+ + padding != getScrollTop() + sectionHeight) {
+ // row should be at the bottom of the viewport
scrollTop = rowTop + getDefaultRowHeight() - sectionHeight
+ padding;
- // ensure that we don't overshoot beyond bottom
- scrollTop = Math.min(scrollTop,
- getRowTop(getRowCount() - 1) + getDefaultRowHeight()
- + spacerContainer
- .getSpacerHeight(getRowCount() - 1)
- - sectionHeight);
} else if (rowTop + getDefaultRowHeight() + spacerHeight
- + padding > getScrollTop() + sectionHeight) {
- // within visual range but end of spacer below the viewport
- // or not enough padding, shift a little
+ + padding != getScrollTop() + sectionHeight) {
+ // spacer should be at the bottom of the viewport
scrollTop = rowTop + getDefaultRowHeight() + spacerHeight
- sectionHeight + padding;
- // ensure that we don't overshoot beyond bottom
- scrollTop = Math.min(scrollTop,
- getRowTop(getRowCount()) - sectionHeight);
} else {
// we are fine where we are
scrollTop = getScrollTop();
+ (spacerHeight / 2.0);
}
scrollTop = center - Math.ceil(sectionHeight / 2.0);
- // ensure that we don't overshoot beyond bottom
- scrollTop = Math.min(scrollTop,
- getRowTop(getRowCount() - 1) + getDefaultRowHeight()
- + spacerContainer
- .getSpacerHeight(getRowCount() - 1)
- - sectionHeight);
- // ensure that we don't overshoot beyond top
- scrollTop = Math.max(0, scrollTop);
break;
case START:
if (!ScrollType.SPACER.equals(scrollType)
- && Math.max(rowTop - padding, 0) < getScrollTop()) {
- // row top above the viewport or not enough padding, shift a
- // little
+ && Math.max(rowTop - padding, 0) != getScrollTop()) {
+ // row should be at the top of the viewport
scrollTop = Math.max(rowTop - padding, 0);
} else if (ScrollType.SPACER.equals(scrollType)
&& Math.max(rowTop + getDefaultRowHeight() - padding,
- 0) < getScrollTop()) {
- // spacer top above the viewport or not enough padding,
- // shift a little
+ 0) != getScrollTop()) {
+ // spacer should be at the top of the viewport
scrollTop = Math
.max(rowTop + getDefaultRowHeight() - padding, 0);
} else {
default:
scrollTop = getScrollTop();
}
- return scrollTop;
+ // ensure that we don't overshoot beyond bottom
+ scrollTop = Math.min(scrollTop,
+ getRowTop(getRowCount()) - sectionHeight);
+ // ensure that we don't overshoot beyond top
+ scrollTop = Math.max(0, scrollTop);
+
+ if (scrollTop != getScrollTop()) {
+ setScrollTop(scrollTop);
+ setBodyScrollPosition(tBodyScrollLeft, scrollTop);
+ }
}
@Override
--- /dev/null
+package com.vaadin.tests.components.grid;
+
+import static org.hamcrest.number.IsCloseTo.closeTo;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import java.util.List;
+
+import org.junit.Test;
+import org.openqa.selenium.WebElement;
+
+import com.vaadin.shared.ui.grid.ScrollDestination;
+import com.vaadin.testbench.By;
+import com.vaadin.testbench.TestBenchElement;
+import com.vaadin.testbench.elements.ButtonElement;
+import com.vaadin.testbench.elements.GridElement;
+import com.vaadin.testbench.elements.NativeSelectElement;
+import com.vaadin.tests.tb3.SingleBrowserTest;
+
+public class GridScrollDestinationTest extends SingleBrowserTest {
+
+ private ButtonElement button;
+ private GridElement grid;
+ private TestBenchElement header;
+ private TestBenchElement tableWrapper;
+
+ @Override
+ public void setup() throws Exception {
+ super.setup();
+ openTestURL();
+ button = $(ButtonElement.class).first();
+ grid = $(GridElement.class).first();
+ header = grid.getHeader();
+ tableWrapper = grid.getTableWrapper();
+ }
+
+ private void assertElementAtTop(WebElement row) {
+ assertThat((double) row.getLocation().getY(), closeTo(
+ header.getLocation().getY() + header.getSize().getHeight(),
+ 1d));
+ }
+
+ private void assertElementAtBottom(WebElement row) {
+ assertThat(
+ (double) row.getLocation().getY() + row.getSize().getHeight(),
+ closeTo((double) tableWrapper.getLocation().getY()
+ + tableWrapper.getSize().getHeight(), 1d));
+ }
+
+ private void assertElementAtMiddle(WebElement row) {
+ assertThat((double) row.getLocation()
+ .getY() + (row.getSize().getHeight() / 2), closeTo(
+ (double) tableWrapper.getLocation().getY()
+ + header.getSize().getHeight()
+ + ((tableWrapper.getSize().getHeight()
+ - header.getSize().getHeight()) / 2),
+ 1d));
+ }
+
+ @Test
+ public void destinationAny() {
+ // ScrollDestination.ANY selected by default
+
+ // scroll down
+ button.click();
+
+ // expect the row at the bottom of the viewport
+ List<WebElement> rows = grid.getBody()
+ .findElements(By.className("v-grid-row"));
+ // last rendered row is a buffer row, inspect second to last
+ WebElement row = rows.get(rows.size() - 2);
+ assertEquals("50", row.getText());
+
+ assertElementAtBottom(row);
+
+ // scroll to end
+ grid.scrollToRow((int) grid.getRowCount() - 1);
+
+ // ensure row 50 is out of visual range, first two rows are out of view
+ // and getText can't find the contents so inspect the third row
+ rows = grid.getBody().findElements(By.className("v-grid-row"));
+ row = rows.get(2);
+
+ assertGreater(row.getText() + " is not greater than 52",
+ Integer.valueOf(row.getText()), 52);
+
+ // scroll up
+ button.click();
+
+ // expect the row at the top of the viewport
+ rows = grid.getBody().findElements(By.className("v-grid-row"));
+ // first rendered row is a buffer row, inspect second
+ row = rows.get(1);
+ assertEquals("50", row.getText());
+
+ assertElementAtTop(row);
+
+ // scroll up by a few rows
+ grid.scrollToRow(45);
+
+ // refresh row references
+ rows = grid.getBody().findElements(By.className("v-grid-row"));
+ row = rows.get(6);
+ assertEquals("50", row.getText());
+
+ // scroll while already within viewport
+ button.click();
+
+ // expect no change since the row is still within viewport
+ rows = grid.getBody().findElements(By.className("v-grid-row"));
+ row = rows.get(6);
+ assertEquals("50", row.getText());
+ }
+
+ @Test
+ public void destinationEnd() {
+ $(NativeSelectElement.class).first()
+ .selectByText(ScrollDestination.END.name());
+
+ // scroll down
+ button.click();
+
+ // expect the row at the bottom of the viewport
+ List<WebElement> rows = grid.getBody()
+ .findElements(By.className("v-grid-row"));
+ // last rendered row is a buffer row, inspect second to last
+ WebElement row = rows.get(rows.size() - 2);
+ assertEquals("50", row.getText());
+
+ assertElementAtBottom(row);
+
+ // scroll to end
+ grid.scrollToRow((int) grid.getRowCount() - 1);
+
+ // ensure row 50 is out of visual range, first two rows are out of view
+ // and getText can't find the contents so inspect the third row
+ rows = grid.getBody().findElements(By.className("v-grid-row"));
+ row = rows.get(2);
+
+ assertGreater(row.getText() + " is not greater than 52",
+ Integer.valueOf(row.getText()), 52);
+
+ // scroll up
+ button.click();
+
+ // expect the row at the bottom of the viewport
+ rows = grid.getBody().findElements(By.className("v-grid-row"));
+ // last rendered row is a buffer row, inspect second to last
+ row = rows.get(rows.size() - 2);
+ assertEquals("50", row.getText());
+
+ assertElementAtBottom(row);
+
+ // scroll down by a few rows
+ grid.scrollToRow(55);
+
+ // refresh row references
+ rows = grid.getBody().findElements(By.className("v-grid-row"));
+ row = rows.get(rows.size() - 7);
+ assertEquals("50", row.getText());
+
+ // scroll while already within viewport
+ button.click();
+
+ // expect the row at the bottom of the viewport again
+ rows = grid.getBody().findElements(By.className("v-grid-row"));
+ row = rows.get(rows.size() - 2);
+ assertEquals("50", row.getText());
+
+ assertElementAtBottom(row);
+ }
+
+ @Test
+ public void destinationStart() {
+ $(NativeSelectElement.class).first()
+ .selectByText(ScrollDestination.START.name());
+
+ // scroll down
+ button.click();
+
+ // expect the row at the top of the viewport
+ List<WebElement> rows = grid.getBody()
+ .findElements(By.className("v-grid-row"));
+ // first rendered row is a buffer row, inspect second
+ WebElement row = rows.get(1);
+ assertEquals("50", row.getText());
+
+ assertElementAtTop(row);
+
+ // scroll to end
+ grid.scrollToRow((int) grid.getRowCount() - 1);
+
+ // ensure row 50 is out of visual range, first two rows are out of view
+ // and getText can't find the contents so inspect the third row
+ rows = grid.getBody().findElements(By.className("v-grid-row"));
+ row = rows.get(2);
+
+ assertGreater(row.getText() + " is not greater than 52",
+ Integer.valueOf(row.getText()), 52);
+
+ // scroll up
+ button.click();
+
+ // expect the row at the top of the viewport
+ rows = grid.getBody().findElements(By.className("v-grid-row"));
+ // first rendered row is a buffer row, inspect second
+ row = rows.get(1);
+ assertEquals("50", row.getText());
+
+ assertElementAtTop(row);
+
+ // scroll up by a few rows
+ grid.scrollToRow(45);
+
+ // refresh row references
+ rows = grid.getBody().findElements(By.className("v-grid-row"));
+ row = rows.get(6);
+ assertEquals("50", row.getText());
+
+ // scroll while already within viewport
+ button.click();
+
+ // expect the row at the top of the viewport again
+ rows = grid.getBody().findElements(By.className("v-grid-row"));
+ row = rows.get(1);
+ assertEquals("50", row.getText());
+
+ assertElementAtTop(row);
+ }
+
+ @Test
+ public void destinationMiddle() {
+ NativeSelectElement destinationSelect = $(NativeSelectElement.class)
+ .first();
+ destinationSelect.selectByText(ScrollDestination.MIDDLE.name());
+
+ // scroll down
+ button.click();
+
+ // expect the row at the middle of the viewport
+ List<WebElement> rows = grid.getBody()
+ .findElements(By.className("v-grid-row"));
+ // inspect the middle row
+ WebElement row = rows.get(rows.size() / 2);
+ assertEquals("50", row.getText());
+
+ assertElementAtMiddle(row);
+
+ // scroll to end
+ grid.scrollToRow((int) grid.getRowCount() - 1);
+
+ // ensure row 50 is out of visual range, first two rows are out of view
+ // and getText can't find the contents so inspect the third row
+ rows = grid.getBody().findElements(By.className("v-grid-row"));
+ row = rows.get(2);
+
+ assertGreater(row.getText() + " is not greater than 52",
+ Integer.valueOf(row.getText()), 52);
+
+ // scroll up
+ button.click();
+
+ // expect the row at the middle of the viewport
+ rows = grid.getBody().findElements(By.className("v-grid-row"));
+ // first rendered row is a buffer row, inspect second
+ row = rows.get(rows.size() / 2);
+ assertEquals("50", row.getText());
+
+ assertElementAtMiddle(row);
+
+ // scroll down by a few rows
+ destinationSelect.selectByText(ScrollDestination.START.name());
+ button.click();
+
+ // refresh row references
+ rows = grid.getBody().findElements(By.className("v-grid-row"));
+ row = rows.get(1);
+ assertEquals("50", row.getText());
+
+ // scroll while already within viewport
+ destinationSelect.selectByText(ScrollDestination.MIDDLE.name());
+ button.click();
+
+ // expect the row at the top of the viewport again
+ rows = grid.getBody().findElements(By.className("v-grid-row"));
+ row = rows.get(rows.size() / 2);
+ assertEquals("50", row.getText());
+
+ assertElementAtMiddle(row);
+ }
+}