Kaynağa Gözat

Restrict grid sidebar size to visible viewport (#19349)

Change-Id: I75b7c662251de53b46e045d17d3cac650586acd2
tags/7.7.0.alpha2
Artur Signell 8 yıl önce
ebeveyn
işleme
0b0495b6d8

+ 1
- 0
client/src/main/java/com/vaadin/client/widgets/Grid.java Dosyayı Görüntüle

@@ -3760,6 +3760,7 @@ public class Grid<T> extends ResizeComposite implements
addStyleName("closed");
}
});
overlay.setFitInWindow(true);
}

/**

+ 133
- 4
client/src/main/java/com/vaadin/client/widgets/Overlay.java Dosyayı Görüntüle

@@ -16,6 +16,9 @@

package com.vaadin.client.widgets;

import java.util.ArrayList;
import java.util.List;

import com.google.gwt.animation.client.Animation;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
@@ -29,6 +32,7 @@ import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.PopupPanel;
@@ -238,6 +242,8 @@ public class Overlay extends PopupPanel implements CloseHandler<PopupPanel> {
@Deprecated
private boolean sinkShadowEvents = false;

private List<Command> runOnClose = new ArrayList<Command>();

public Overlay() {
super();
adjustZIndex();
@@ -362,6 +368,58 @@ public class Overlay extends PopupPanel implements CloseHandler<PopupPanel> {

@Override
public void setPopupPosition(int left, int top) {

// PopupPanel tries to position the popup on screen (by
// default right, below) and will move it if there is not
// enough space right or below but only if there is
// sufficient space left or above. If the popup is too big
// to fit on either side, it will be in the original
// position.

if (isFitInWindow()) {
int windowLeft = Window.getScrollLeft();
int windowRight = Window.getScrollLeft() + Window.getClientWidth();
int width = getOffsetWidth();
int popupRight = left + width;
int popupRightOfWindow = popupRight - windowRight;
if (popupRightOfWindow > 0) {
// Popup is too large to fit
left -= popupRightOfWindow;
if (left < 0) {
// Would move left of screen, shrink to fit in window
setOuterWidthThroughWidget(windowRight - windowLeft);
runOnClose.add(new Command() {
@Override
public void execute() {
getWidget().setWidth("");
}
});
left = 0;
}
}

int windowTop = Window.getScrollTop();
int windowBottom = Window.getScrollTop() + Window.getClientHeight();
int height = getOffsetHeight();
int popupBottom = top + height;
int popupBelowWindow = popupBottom - windowBottom;
if (popupBelowWindow > 0) {
// Popup is too large to fit
top -= popupBelowWindow;
if (top < 0) {
// Would move above screen, shrink to fit in window
setOuterHeightThroughWidget(windowBottom - windowTop);
runOnClose.add(new Command() {
@Override
public void execute() {
getWidget().setHeight("");
}
});
top = 0;
}
}
}

// TODO, this should in fact be part of
// Document.get().getBodyOffsetLeft/Top(). Would require overriding DOM
// for all permutations. Now adding fix as margin instead of fixing
@@ -373,6 +431,30 @@ public class Overlay extends PopupPanel implements CloseHandler<PopupPanel> {
positionOrSizeUpdated(isAnimationEnabled() ? 0 : 1);
}

private void setOuterHeightThroughWidget(int outerHeight) {
getWidget().setHeight(outerHeight + "px");

// Take margin/border/padding into account if needed
// (the height is for the overlay root but we set it on the
// widget)
int adjustedHeight = outerHeight - (getOffsetHeight() - outerHeight);
if (adjustedHeight != outerHeight) {
getWidget().setHeight(adjustedHeight + "px");
}
}

private void setOuterWidthThroughWidget(int outerWidth) {
getWidget().setWidth(outerWidth + "px");

// Take margin/border/padding into account if needed
// (the height is for the overlay root but we set it on the
// widget)
int adjustedWidth = outerWidth - (getOffsetWidth() - outerWidth);
if (adjustedWidth != outerWidth) {
getWidget().setWidth(adjustedWidth + "px");
}
}

private IFrameElement getShimElement() {
if (shimElement == null && needsShimElement()) {
shimElement = Document.get().createIFrameElement();
@@ -465,6 +547,8 @@ public class Overlay extends PopupPanel implements CloseHandler<PopupPanel> {

private JavaScriptObject animateInListener;

private boolean fitInWindow = false;

private boolean maybeShowWithAnimation() {
boolean isAttached = isAttached() && isShowing();
super.show();
@@ -933,7 +1017,7 @@ public class Overlay extends PopupPanel implements CloseHandler<PopupPanel> {
public void hide(final boolean autoClosed, final boolean animateIn,
final boolean animateOut) {
if (BrowserInfo.get().isIE8() || BrowserInfo.get().isIE9()) {
super.hide(autoClosed);
reallyHide(autoClosed);
} else {
if (animateIn
&& getStyleName().contains(ADDITIONAL_CLASSNAME_ANIMATE_IN)) {
@@ -945,7 +1029,7 @@ public class Overlay extends PopupPanel implements CloseHandler<PopupPanel> {
.getAnimationName(event)
.contains(
ADDITIONAL_CLASSNAME_ANIMATE_IN)) {
Overlay.this.hide(autoClosed);
reallyHide(autoClosed);
}
}
});
@@ -990,7 +1074,7 @@ public class Overlay extends PopupPanel implements CloseHandler<PopupPanel> {
+ "-"
+ ADDITIONAL_CLASSNAME_ANIMATE_OUT);
}
Overlay.super.hide(autoClosed);
reallyHide(autoClosed);
}
}
});
@@ -1003,10 +1087,55 @@ public class Overlay extends PopupPanel implements CloseHandler<PopupPanel> {
shadow.removeClassName(CLASSNAME_SHADOW + "-"
+ ADDITIONAL_CLASSNAME_ANIMATE_OUT);
}
super.hide(autoClosed);
reallyHide(autoClosed);
}
}
}
}

private void reallyHide(boolean autoClosed) {
super.hide(autoClosed);
for (Command c : runOnClose) {
c.execute();
}
runOnClose.clear();
}

/**
* Sets whether the overlay should be moved or shrunk to fit inside the
* window.
* <p>
* When this is <code>false</code>, the default {@link PopupPanel} behavior
* is used, which tries to position the popup primarly below and to the
* right of a reference UIObject and, if there is not enough space, above or
* to the left.
* <p>
* When this is <code>true</code>, the popup will be moved up/left in case
* it does not fit on either side. If the popup is larger than the window,
* it will be shrunk to fit and assume that scrolling e.g. using
* <code>overflow:auto</code>, is taken care of by the overlay user.
*
* @since
* @param fitInWindow
* <code>true</code> to ensure that no part of the popup is
* outside the visible view, <code>false</code> to use the
* default {@link PopupPanel} behavior
*/
public void setFitInWindow(boolean fitInWindow) {
this.fitInWindow = fitInWindow;
}

/**
* Checks whether the overlay should be moved or shrunk to fit inside the
* window.
*
* @see #setFitInWindow(boolean)
*
* @since
* @return <code>true</code> if the popup will be moved and/or shrunk to fit
* inside the window, <code>false</code> otherwise
*/
public boolean isFitInWindow() {
return fitInWindow;
}
}

+ 2
- 0
themes/src/main/themes/VAADIN/themes/base/grid/grid.scss Dosyayı Görüntüle

@@ -158,6 +158,8 @@ $v-grid-details-border-bottom-stripe: 1px solid darken($v-grid-row-background-co

.#{$primaryStyleName}-sidebar-content {
padding: 4px 0;
overflow-y: auto;
overflow-x: hidden;

.gwt-MenuBar {
.gwt-MenuItem .column-hiding-toggle {

+ 2
- 0
themes/src/main/themes/VAADIN/themes/valo/components/_grid.scss Dosyayı Görüntüle

@@ -228,6 +228,8 @@ $v-grid-details-border-bottom-stripe: $v-grid-cell-horizontal-border !default;
.#{$primary-stylename}-sidebar-content {
margin: 0 0 2px;
padding: 4px 4px 2px;
overflow-y: auto;
overflow-x: hidden;
}
}


+ 69
- 0
uitest/src/main/java/com/vaadin/tests/components/grid/GridSidebarPosition.java Dosyayı Görüntüle

@@ -0,0 +1,69 @@
/*
* Copyright 2000-2014 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.annotations.Theme;
import com.vaadin.server.VaadinRequest;
import com.vaadin.tests.components.AbstractTestUI;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Grid;
import com.vaadin.ui.HorizontalLayout;

@Theme("valo")
public class GridSidebarPosition extends AbstractTestUI {

static final String POPUP_ABOVE = "above";
static final String POPUP_WINDOW_MOVED_UP = "movedup";
static final String POPUP_WINDOW_HEIGHT = "windowheight";

@Override
protected void setup(VaadinRequest request) {
HorizontalLayout hl = new HorizontalLayout();
hl.setSpacing(true);
hl.setHeight("100%");
setContent(hl);
Grid grid = new Grid("Popup window height");
grid.setId(POPUP_WINDOW_HEIGHT);
grid.setWidth("100px");
for (int i = 0; i < 30; i++) {
grid.addColumn(
"This is a really really really really long column name "
+ i).setHidable(true);
}
hl.addComponent(grid);

grid = new Grid("Popup moved up");
grid.setId(POPUP_WINDOW_MOVED_UP);
grid.setWidth("100px");
grid.setHeight("400px");
for (int i = 0; i < 15; i++) {
grid.addColumn("Column " + i).setHidable(true);
}
hl.addComponent(grid);
hl.setComponentAlignment(grid, Alignment.BOTTOM_LEFT);

grid = new Grid("Popup above");
grid.setId(POPUP_ABOVE);
grid.setWidth("100px");
grid.setHeight("200px");
for (int i = 0; i < 10; i++) {
grid.addColumn("Column " + i).setHidable(true);
}
hl.addComponent(grid);
hl.setComponentAlignment(grid, Alignment.BOTTOM_LEFT);
}

}

+ 95
- 0
uitest/src/test/java/com/vaadin/tests/components/grid/GridSidebarPositionTest.java Dosyayı Görüntüle

@@ -0,0 +1,95 @@
/*
* Copyright 2000-2014 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.List;

import org.junit.Assert;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.Point;
import org.openqa.selenium.WebElement;

import com.vaadin.testbench.elements.GridElement;
import com.vaadin.testbench.parallel.BrowserUtil;
import com.vaadin.tests.tb3.MultiBrowserTest;

public class GridSidebarPositionTest extends MultiBrowserTest {

@Test
public void heightRestrictedToBrowserWindow() {
openTestURL();
GridElement gridWithVeryManyColumns = $(GridElement.class).id(
GridSidebarPosition.POPUP_WINDOW_HEIGHT);
getSidebarOpenButton(gridWithVeryManyColumns).click();
Dimension popupSize = getSidebarPopup().getSize();
Dimension browserWindowSize = getDriver().manage().window().getSize();

Assert.assertTrue(popupSize.getHeight() <= browserWindowSize
.getHeight());
}

@Test
public void popupNotBelowBrowserWindow() {
openTestURL();
GridElement gridAtBottom = $(GridElement.class).id(
GridSidebarPosition.POPUP_WINDOW_MOVED_UP);
getSidebarOpenButton(gridAtBottom).click();
WebElement sidebarPopup = getSidebarPopup();
Dimension popupSize = sidebarPopup.getSize();
Point popupLocation = sidebarPopup.getLocation();
int popupBottom = popupLocation.getY() + popupSize.getHeight();
Dimension browserWindowSize = getDriver().manage().window().getSize();

Assert.assertTrue(popupBottom <= browserWindowSize.getHeight());
}

@Test
public void popupAbove() {
openTestURL();
GridElement gridPopupAbove = $(GridElement.class).id(
GridSidebarPosition.POPUP_ABOVE);
WebElement sidebarOpenButton = getSidebarOpenButton(gridPopupAbove);
sidebarOpenButton.click();
WebElement sidebarPopup = getSidebarPopup();
Dimension popupSize = sidebarPopup.getSize();
Point popupLocation = sidebarPopup.getLocation();
int popupBottom = popupLocation.getY() + popupSize.getHeight();
int sideBarButtonTop;
if (BrowserUtil.isIE8(getDesiredCapabilities())) {
// IE8 gets the top coordinate for the button completely wrong for
// some reason
sideBarButtonTop = 660;
} else {
sideBarButtonTop = sidebarOpenButton.getLocation().getY();
}
Assert.assertTrue(popupBottom <= sideBarButtonTop);
}

protected WebElement getSidebarOpenButton(GridElement grid) {
List<WebElement> elements = grid.findElements(By
.className("v-grid-sidebar-button"));
return elements.isEmpty() ? null : elements.get(0);
}

protected WebElement getSidebarPopup() {
List<WebElement> elements = findElements(By
.className("v-grid-sidebar-popup"));
return elements.isEmpty() ? null : elements.get(0);
}

}

Loading…
İptal
Kaydet