diff options
7 files changed, 301 insertions, 93 deletions
diff --git a/client/src/main/java/com/vaadin/client/widget/escalator/ColumnConfiguration.java b/client/src/main/java/com/vaadin/client/widget/escalator/ColumnConfiguration.java index 51e8663749..8488981a76 100644 --- a/client/src/main/java/com/vaadin/client/widget/escalator/ColumnConfiguration.java +++ b/client/src/main/java/com/vaadin/client/widget/escalator/ColumnConfiguration.java @@ -143,7 +143,8 @@ public interface ColumnConfiguration { public double getColumnWidth(int index) throws IllegalArgumentException; /** - * Sets widths for a set of columns. + * Sets widths for a set of columns. Triggers element size recalculation for + * elements that require manual calculations. * * @param indexWidthMap * a map from column index to its respective width to be set. If @@ -160,6 +161,26 @@ public interface ColumnConfiguration { throws IllegalArgumentException; /** + * Sets widths for a set of columns. + * + * @param indexWidthMap + * a map from column index to its respective width to be set. If + * the given width for a column index is negative, the column is + * resized-to-fit. + * @param recalculateElementSizes + * should the element size recalculation be triggered for + * elements that require manual calculation + * @throws IllegalArgumentException + * if {@code indexWidthMap} is {@code null} + * @throws IllegalArgumentException + * if any column index in {@code indexWidthMap} is invalid + * @throws NullPointerException + * If any value in the map is <code>null</code> + */ + public void setColumnWidths(Map<Integer, Double> indexWidthMap, + boolean recalculateElementSizes) throws IllegalArgumentException; + + /** * Returns the actual width of a column. * * @param index diff --git a/client/src/main/java/com/vaadin/client/widget/escalator/ScrollbarBundle.java b/client/src/main/java/com/vaadin/client/widget/escalator/ScrollbarBundle.java index 122c302f36..3a01c267fc 100644 --- a/client/src/main/java/com/vaadin/client/widget/escalator/ScrollbarBundle.java +++ b/client/src/main/java/com/vaadin/client/widget/escalator/ScrollbarBundle.java @@ -354,8 +354,6 @@ public abstract class ScrollbarBundle implements DeferredWorker { private final ScrollEventFirer scrollEventFirer = new ScrollEventFirer(); - private HandlerRegistration scrollSizeTemporaryScrollHandler; - private HandlerRegistration offsetSizeTemporaryScrollHandler; private HandlerRegistration scrollInProgress; private ScrollbarBundle() { @@ -417,27 +415,18 @@ public abstract class ScrollbarBundle implements DeferredWorker { * * @param px * the length of the scrollbar in pixels + * @see #setOffsetSizeAndScrollSize(double, double) */ public final void setOffsetSize(final double px) { - /* - * This needs to be made step-by-step because IE8 flat-out refuses to - * fire a scroll event when the scroll size becomes smaller than the - * offset size. All other browser need to suffer alongside. - */ - boolean newOffsetSizeIsGreaterThanScrollSize = px > getScrollSize(); boolean offsetSizeBecomesGreaterThanScrollSize = showsScrollHandle() && newOffsetSizeIsGreaterThanScrollSize; + if (offsetSizeBecomesGreaterThanScrollSize && getScrollPos() != 0) { - if (offsetSizeTemporaryScrollHandler != null) { - offsetSizeTemporaryScrollHandler.removeHandler(); - } - // must be a field because Java insists. - offsetSizeTemporaryScrollHandler = addScrollHandler( - event -> setOffsetSizeNow(px)); setScrollPos(0); - } else { + setOffsetSizeNow(px); + } else if (px != getOffsetSize()) { setOffsetSizeNow(px); } } @@ -447,9 +436,50 @@ public abstract class ScrollbarBundle implements DeferredWorker { recalculateMaxScrollPos(); forceScrollbar(showsScrollHandle()); fireVisibilityChangeIfNeeded(); - if (offsetSizeTemporaryScrollHandler != null) { - offsetSizeTemporaryScrollHandler.removeHandler(); - offsetSizeTemporaryScrollHandler = null; + } + + /** + * Sets the length of the scrollbar and the amount of pixels the scrollbar + * needs to be able to scroll through. + * + * @param offsetPx + * the length of the scrollbar in pixels + * @param scrollPx + * the number of pixels the scrollbar should be able to scroll + * through + */ + public final void setOffsetSizeAndScrollSize(final double offsetPx, + final double scrollPx) { + + boolean newOffsetSizeIsGreaterThanScrollSize = offsetPx > scrollPx; + boolean offsetSizeBecomesGreaterThanScrollSize = showsScrollHandle() + && newOffsetSizeIsGreaterThanScrollSize; + + boolean needsMoreHandling = false; + if (offsetSizeBecomesGreaterThanScrollSize && getScrollPos() != 0) { + setScrollPos(0); + if (offsetPx != getOffsetSize()) { + internalSetOffsetSize(Math.max(0, offsetPx)); + } + if (scrollPx != getScrollSize()) { + internalSetScrollSize(Math.max(0, scrollPx)); + } + needsMoreHandling = true; + } else { + if (offsetPx != getOffsetSize()) { + internalSetOffsetSize(Math.max(0, offsetPx)); + needsMoreHandling = true; + } + if (scrollPx != getScrollSize()) { + internalSetScrollSize(Math.max(0, scrollPx)); + needsMoreHandling = true; + } + } + + if (needsMoreHandling) { + recalculateMaxScrollPos(); + forceScrollbar(showsScrollHandle()); + fireVisibilityChangeIfNeeded(); } } @@ -626,43 +656,18 @@ public abstract class ScrollbarBundle implements DeferredWorker { * @param px * the number of pixels the scrollbar should be able to scroll * through + * @see #setOffsetSizeAndScrollSize(double, double) */ public final void setScrollSize(final double px) { - /* - * This needs to be made step-by-step because IE8 flat-out refuses to - * fire a scroll event when the scroll size becomes smaller than the - * offset size. All other browser need to suffer alongside. - * - * This really should be changed to not use any temporary scroll - * handlers at all once IE8 support is dropped, like now done only for - * Firefox. - */ - boolean newScrollSizeIsSmallerThanOffsetSize = px <= getOffsetSize(); boolean scrollSizeBecomesSmallerThanOffsetSize = showsScrollHandle() && newScrollSizeIsSmallerThanOffsetSize; + if (scrollSizeBecomesSmallerThanOffsetSize && getScrollPos() != 0) { - /* - * For whatever reason, Firefox loses the scroll event in this case - * and the onscroll handler is never called (happens when reducing - * size from 1000 items to 1 while being scrolled a bit down, see - * #19802). Based on the comment above, only IE8 should really use - * 'delayedSizeSet' - */ - boolean delayedSizeSet = !BrowserInfo.get().isFirefox(); - if (delayedSizeSet) { - if (scrollSizeTemporaryScrollHandler != null) { - scrollSizeTemporaryScrollHandler.removeHandler(); - } - scrollSizeTemporaryScrollHandler = addScrollHandler( - event -> setScrollSizeNow(px)); - } setScrollPos(0); - if (!delayedSizeSet) { - setScrollSizeNow(px); - } - } else { + setScrollSizeNow(px); + } else if (px != getScrollSize()) { setScrollSizeNow(px); } } @@ -672,10 +677,6 @@ public abstract class ScrollbarBundle implements DeferredWorker { recalculateMaxScrollPos(); forceScrollbar(showsScrollHandle()); fireVisibilityChangeIfNeeded(); - if (scrollSizeTemporaryScrollHandler != null) { - scrollSizeTemporaryScrollHandler.removeHandler(); - scrollSizeTemporaryScrollHandler = null; - } } /** @@ -913,8 +914,6 @@ public abstract class ScrollbarBundle implements DeferredWorker { public boolean isWorkPending() { // Need to include scrollEventFirer.isBeingFired as it might use // requestAnimationFrame - which is not automatically checked - return scrollSizeTemporaryScrollHandler != null - || offsetSizeTemporaryScrollHandler != null - || scrollInProgress != null || scrollEventFirer.isBeingFired; + return scrollInProgress != null || scrollEventFirer.isBeingFired; } } 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 61dbbf6dd9..a5046ce970 100644 --- a/client/src/main/java/com/vaadin/client/widgets/Escalator.java +++ b/client/src/main/java/com/vaadin/client/widgets/Escalator.java @@ -867,8 +867,8 @@ public class Escalator extends Widget double headerHeight = header.getHeightOfSection(); double vScrollbarHeight = Math.max(0, tableWrapperHeight - footerHeight - headerHeight); - verticalScrollbar.setOffsetSize(vScrollbarHeight); - verticalScrollbar.setScrollSize(scrollContentHeight); + verticalScrollbar.setOffsetSizeAndScrollSize(vScrollbarHeight, + scrollContentHeight); /* * If decreasing the amount of frozen columns, and scrolled to the @@ -884,8 +884,8 @@ public class Escalator extends Widget columnConfiguration.getColumnCount())); double frozenPixels = scrollContentWidth - unfrozenPixels; double hScrollOffsetWidth = tableWrapperWidth - frozenPixels; - horizontalScrollbar.setOffsetSize(hScrollOffsetWidth); - horizontalScrollbar.setScrollSize(unfrozenPixels); + horizontalScrollbar.setOffsetSizeAndScrollSize(hScrollOffsetWidth, + unfrozenPixels); horizontalScrollbar.getElement().getStyle().setLeft(frozenPixels, Unit.PX); horizontalScrollbar.setScrollPos(prevScrollPos); @@ -1524,7 +1524,8 @@ public class Escalator extends Widget Integer col = Integer.valueOf(i); colWidths.put(col, width); } - getColumnConfiguration().setColumnWidths(colWidths); + getColumnConfiguration().setColumnWidths(colWidths, + true); }); } } @@ -2486,7 +2487,7 @@ public class Escalator extends Widget */ verticalScrollbar.setOffsetSize( heightOfEscalator - header.getHeightOfSection() - - footer.getHeightOfSection()); + - footer.getHeightOfSection() + 1); body.verifyEscalatorCount(); body.spacerContainer.updateSpacerDecosVisibility(); @@ -2604,21 +2605,8 @@ public class Escalator extends Widget @Override protected void sectionHeightCalculated() { - double headerHeight = header.getHeightOfSection(); - double footerHeight = footer.getHeightOfSection(); - int vscrollHeight = (int) Math - .floor(heightOfEscalator - headerHeight - footerHeight); - - final boolean horizontalScrollbarNeeded = columnConfiguration - .calculateRowWidth() > widthOfEscalator; - if (horizontalScrollbarNeeded) { - vscrollHeight -= horizontalScrollbar.getScrollbarThickness(); - } - footerDeco.getStyle().setHeight(footer.getHeightOfSection(), Unit.PX); - - verticalScrollbar.setOffsetSize(vscrollHeight); } } @@ -5745,7 +5733,7 @@ public class Escalator extends Widget Integer col = Integer.valueOf(i); colWidths.put(col, width); } - getColumnConfiguration().setColumnWidths(colWidths); + getColumnConfiguration().setColumnWidths(colWidths, true); } // Adjust scrollbar @@ -5839,12 +5827,19 @@ public class Escalator extends Widget public void setColumnWidth(int index, double px) throws IllegalArgumentException { setColumnWidths(Collections.singletonMap(Integer.valueOf(index), - Double.valueOf(px))); + Double.valueOf(px)), true); } @Override public void setColumnWidths(Map<Integer, Double> indexWidthMap) throws IllegalArgumentException { + setColumnWidths(indexWidthMap, true); + } + + @Override + public void setColumnWidths(Map<Integer, Double> indexWidthMap, + boolean recalculateElementSizes) + throws IllegalArgumentException { if (indexWidthMap == null) { throw new IllegalArgumentException("indexWidthMap was null"); @@ -5874,7 +5869,9 @@ public class Escalator extends Widget body.reapplyColumnWidths(); footer.reapplyColumnWidths(); - recalculateElementSizes(); + if (recalculateElementSizes) { + recalculateElementSizes(); + } } finally { Profiler.leave( 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 a47cc4bfca..d4c93751ea 100755 --- a/client/src/main/java/com/vaadin/client/widgets/Grid.java +++ b/client/src/main/java/com/vaadin/client/widgets/Grid.java @@ -3462,10 +3462,27 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, private boolean columnsAreGuaranteedToBeWiderThanGrid() { double freeSpace = escalator.getInnerWidth(); for (Column<?, ?> column : getVisibleColumns()) { + /* + * Check the width and min width and ensure that no column can + * be expected to be narrower than what the resize handler + * requires, if one is present. + */ if (column.getWidth() >= 0) { - freeSpace -= column.getWidth(); + if (column.isResizable() && resizeHandleWidth > 0) { + freeSpace -= Math.max(resizeHandleWidth, + column.getWidth()); + } else { + freeSpace -= column.getWidth(); + } } else if (column.getMinimumWidth() >= 0) { - freeSpace -= column.getMinimumWidth(); + if (column.isResizable() && resizeHandleWidth > 0) { + freeSpace -= Math.max(resizeHandleWidth, + column.getMinimumWidth()); + } else { + freeSpace -= column.getMinimumWidth(); + } + } else if (column.isResizable() && resizeHandleWidth > 0) { + freeSpace -= resizeHandleWidth; } } return freeSpace < 0; @@ -3482,7 +3499,7 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, selfWidths.put(index, columns.get(index).getWidth()); } Grid.this.escalator.getColumnConfiguration() - .setColumnWidths(selfWidths); + .setColumnWidths(selfWidths, true); /* * Step 2: Make sure that each column ends up obeying their min/max @@ -3508,7 +3525,7 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, } } Grid.this.escalator.getColumnConfiguration() - .setColumnWidths(constrainedWidths); + .setColumnWidths(constrainedWidths, true); } private void applyColumnWidthsWithExpansion() { @@ -3530,15 +3547,21 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, for (Column<?, T> column : visibleColumns) { final double widthAsIs = column.getWidth(); final boolean isFixedWidth = widthAsIs >= 0; - // Check for max width just to be sure we don't break the limits - final double widthFixed = Math.max( - Math.min(getMaxWidth(column), widthAsIs), - column.getMinimumWidth()); defaultExpandRatios = defaultExpandRatios && (column.getExpandRatio() == -1 || column == selectionColumn); if (isFixedWidth) { + // Check for min & max width just to be sure we don't break + // the limits + double widthFixed = Math.max( + Math.min(getMaxWidth(column), widthAsIs), + column.getMinimumWidth()); + if (column.isResizable() && resizeHandleWidth > 0) { + // Ensure the resize handle fits + widthFixed = Math.max(widthFixed, resizeHandleWidth); + } + columnSizes.put(visibleColumns.indexOf(column), widthFixed); reservedPixels += widthFixed; } else { @@ -3547,7 +3570,13 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, } } - setColumnSizes(columnSizes); + /* + * Set column sizes so that it's possible to measure non-fixed + * actual sizes without previously applied expand ratio tweaks, but + * don't trigger the element size recalculation before the rest of + * this method has also been processed. + */ + setColumnSizes(columnSizes, false); for (Column<?, T> column : nonFixedColumns) { final int expandRatio = defaultExpandRatios ? 1 @@ -3555,9 +3584,21 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, final double maxWidth = getMaxWidth(column); double newWidth; if (column.isMinimumWidthFromContent()) { - newWidth = Math.min(maxWidth, column.getWidthActual()); + if (column.isResizable() && resizeHandleWidth > 0) { + // Ensure the resize handle fits + newWidth = Math.max( + Math.min(maxWidth, column.getWidthActual()), + resizeHandleWidth); + } else { + newWidth = Math.min(maxWidth, column.getWidthActual()); + } } else { - newWidth = 0; + if (column.isResizable() && resizeHandleWidth > 0) { + // Ensure the resize handle fits + newWidth = resizeHandleWidth; + } else { + newWidth = 0; + } } boolean shouldExpand = newWidth < maxWidth && expandRatio > 0 @@ -3580,8 +3621,15 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, if (pixelsToDistribute <= 0 || totalRatios <= 0) { if (pixelsToDistribute <= 0) { // Set column sizes for expanding columns - setColumnSizes(columnSizes); + setColumnSizes(columnSizes, true); } + /* + * If pixelsToDistribute > 0 the element size recalculation + * isn't done at all, even if some column sizes were set + * earlier, but this doesn't appear to be detrimental while + * attempting to trigger the recalculation here breaks a + * GridEditRow test. + */ return; } @@ -3617,7 +3665,7 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, } while (aColumnHasMaxedOut); if (totalRatios <= 0 && columnsToExpand.isEmpty()) { - setColumnSizes(columnSizes); + setColumnSizes(columnSizes, true); return; } assert pixelsToDistribute > 0 : "We've run out of pixels to distribute (" @@ -3722,12 +3770,14 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, } while (minWidthsCausedReflows); // Finally set all the column sizes. - setColumnSizes(columnSizes); + setColumnSizes(columnSizes, true); } - private void setColumnSizes(Map<Integer, Double> columnSizes) { + private void setColumnSizes(Map<Integer, Double> columnSizes, + boolean recalculateElementSizes) { // Set all widths at once - escalator.getColumnConfiguration().setColumnWidths(columnSizes); + escalator.getColumnConfiguration().setColumnWidths(columnSizes, + recalculateElementSizes); } private int getExpandRatio(Column<?, ?> column, @@ -4389,6 +4439,8 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, private boolean refreshBodyRequested = false; + private double resizeHandleWidth = 0; + private DragAndDropHandler.DragAndDropCallback headerCellDndCallback = new DragAndDropCallback() { private final AutoScrollerCallback autoScrollerCallback = new AutoScrollerCallback() { @@ -6056,6 +6108,12 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, final DragHandle dragger = new DragHandle( getStylePrimaryName() + "-column-resize-handle"); dragger.addTo(td); + // Save the newest resize handle's width with the assumption + // that all the resize handles are the same size. This is + // used in column's minimum width calculations, so the + // border of the cell is also included. + resizeHandleWidth = dragger.getElement().getOffsetWidth() + + WidgetUtil.getBorderLeftAndRightThickness(td); // Common functionality for drag handle callback // implementations diff --git a/uitest/src/main/java/com/vaadin/tests/components/grid/GridColumnsNoMinimumWidthFromContent.java b/uitest/src/main/java/com/vaadin/tests/components/grid/GridColumnsNoMinimumWidthFromContent.java new file mode 100644 index 0000000000..e5a88f8092 --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/grid/GridColumnsNoMinimumWidthFromContent.java @@ -0,0 +1,66 @@ +package com.vaadin.tests.components.grid; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Grid; +import com.vaadin.ui.components.grid.FooterRow; + +public class GridColumnsNoMinimumWidthFromContent extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + Random random = new Random(); + + List<DummyGridRow> gridRows = new ArrayList<DummyGridRow>(); + gridRows.add(new DummyGridRow(random)); + + Grid<DummyGridRow> grid = new Grid<DummyGridRow>(); + for (int i = 0; i < 20; i++) { + grid.addColumn(DummyGridRow::getValue) + .setCaption("[" + i + "] Quite dummy column") + .setMinimumWidthFromContent(false); + } + + grid.setItems(gridRows); + FooterRow defaultFooter = grid.appendFooterRow(); + grid.getColumns().forEach(column -> defaultFooter.getCell(column) + .setText(grid.getDefaultHeaderRow().getCell(column).getText())); + grid.setFooterVisible(true); + grid.setHeightByRows(gridRows.size()); + grid.setWidthFull(); + + getLayout().addComponent(grid); + } + + class DummyGridRow { + private Random random = null; + + public DummyGridRow(Random random) { + this.random = random; + } + + public int getValue() { + return random.nextInt(1000000000); + } + } + + @Override + protected Integer getTicketNumber() { + return 12139; + } + + @Override + protected String getTestDescription() { + return "Loading the UI should not get stuck in an eternal loop " + + "and the columns should be narrow with ellipsis " + + "until the page is resized small enough that " + + "the resize handles alone force a scrollbar. " + + "No overflowing of header cells should occur " + + "when resized very near to the cutoff point " + + "between no scrollbar and a scrollbar."; + } +} diff --git a/uitest/src/main/java/com/vaadin/tests/widgetset/client/grid/EscalatorProxy.java b/uitest/src/main/java/com/vaadin/tests/widgetset/client/grid/EscalatorProxy.java index 5d5148109b..65fe75e9fa 100644 --- a/uitest/src/main/java/com/vaadin/tests/widgetset/client/grid/EscalatorProxy.java +++ b/uitest/src/main/java/com/vaadin/tests/widgetset/client/grid/EscalatorProxy.java @@ -86,6 +86,14 @@ public class EscalatorProxy extends Escalator { throws IllegalArgumentException { columnConfiguration.setColumnWidths(indexWidthMap); } + + @Override + public void setColumnWidths(Map<Integer, Double> indexWidthMap, + boolean recalculateElementSizes) + throws IllegalArgumentException { + columnConfiguration.setColumnWidths(indexWidthMap, + recalculateElementSizes); + } } private class BodyRowContainerProxy extends RowContainerProxy diff --git a/uitest/src/test/java/com/vaadin/tests/components/grid/GridColumnsNoMinimumWidthFromContentTest.java b/uitest/src/test/java/com/vaadin/tests/components/grid/GridColumnsNoMinimumWidthFromContentTest.java new file mode 100644 index 0000000000..6ae15fdb13 --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/grid/GridColumnsNoMinimumWidthFromContentTest.java @@ -0,0 +1,59 @@ +package com.vaadin.tests.components.grid; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.number.IsCloseTo.closeTo; +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.openqa.selenium.Dimension; +import org.openqa.selenium.WebElement; + +import com.vaadin.testbench.By; +import com.vaadin.testbench.elements.GridElement; +import com.vaadin.testbench.elements.GridElement.GridCellElement; +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class GridColumnsNoMinimumWidthFromContentTest extends MultiBrowserTest { + + @Test + public void testResizing() { + openTestURL(); + + GridElement grid = $(GridElement.class).first(); + WebElement hScrollbar = grid + .findElement(By.className("v-grid-scroller-horizontal")); + + // initial state, should have no scrollbar + GridCellElement lastColumn = grid.getHeaderCell(0, 19); + ensureScrollbarVisibility(hScrollbar, false); + ensureNoGap(grid, lastColumn); + + // resize small enough to get a scrollbar + getDriver().manage().window().setSize(new Dimension(810, 800)); + ensureScrollbarVisibility(hScrollbar, true); + + // resize just enough to lose the scrollbar + getDriver().manage().window().setSize(new Dimension(840, 800)); + ensureScrollbarVisibility(hScrollbar, false); + ensureNoGap(grid, lastColumn); + + int lastColumnWidth = lastColumn.getSize().getWidth(); + assertGreater("Unexpected last column width: " + lastColumnWidth + + " (should be over 30)", lastColumnWidth, 30); + } + + private void ensureNoGap(GridElement grid, GridCellElement lastColumn) { + int gridRightEdge = grid.getLocation().getX() + + grid.getSize().getWidth(); + int lastColumnRightEdge = lastColumn.getLocation().getX() + + lastColumn.getSize().getWidth(); + assertThat("Unexpected positioning.", (double) gridRightEdge, + closeTo(lastColumnRightEdge, 1d)); + } + + private void ensureScrollbarVisibility(WebElement scrollbar, + boolean displayed) { + assertEquals(displayed ? "block" : "none", + scrollbar.getCssValue("display")); + } +} |