]> source.dussan.org Git - vaadin-framework.git/commitdiff
Position tooltips in the visible area (#15129).
authorAnna Miroshnik <anna.miroshnik@arcadia.spb.ru>
Tue, 25 Nov 2014 09:19:05 +0000 (12:19 +0300)
committerSauli Tähkäpää <sauli@vaadin.com>
Fri, 28 Nov 2014 10:30:26 +0000 (12:30 +0200)
Based on Mika's reverted patch, with additional fix and test for
regression "an empty tooltip appears while the application is
initializing".

Change-Id: I8237fc9340265708a05a7576a5d9e8e374dc1fea

client/src/com/vaadin/client/VTooltip.java
uitest/src/com/vaadin/tests/components/TooltipPosition.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/TooltipPositionTest.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/menubar/MenuTooltipTest.java

index edd1273bf5ad7a4bb813c229543e894eb6ddc592..47a1b71228c8e97c2dea881f7ab2059a91a55d83 100644 (file)
@@ -210,6 +210,14 @@ public class VTooltip extends VOverlay {
                         x = Window.getClientWidth() - offsetWidth - MARGIN
                                 + Window.getScrollLeft();
                     }
+
+                    if (tooltipEventMouseX != EVENT_XY_POSITION_OUTSIDE) {
+                        // Do not allow x to be zero, for otherwise the tooltip
+                        // does not close when the mouse is moved (see
+                        // isTooltipOpen()). #15129
+                        int minX = Window.getScrollLeft() + MARGIN;
+                        x = Math.max(x, minX);
+                    }
                     return x;
                 }
 
@@ -245,6 +253,14 @@ public class VTooltip extends VOverlay {
                             y = Window.getScrollTop();
                         }
                     }
+
+                    if (tooltipEventMouseY != EVENT_XY_POSITION_OUTSIDE) {
+                        // Do not allow y to be zero, for otherwise the tooltip
+                        // does not close when the mouse is moved (see
+                        // isTooltipOpen()). #15129
+                        int minY = Window.getScrollTop() + MARGIN;
+                        y = Math.max(y, minY);
+                    }
                     return y;
                 }
             });
@@ -323,6 +339,7 @@ public class VTooltip extends VOverlay {
         setPopupPosition(tooltipEventMouseX, tooltipEventMouseY);
     }
 
+    private int EVENT_XY_POSITION_OUTSIDE = -5000;
     private int tooltipEventMouseX;
     private int tooltipEventMouseY;
 
@@ -332,11 +349,13 @@ public class VTooltip extends VOverlay {
     }
 
     private int getEventX(Event event, boolean isFocused) {
-        return isFocused ? -5000 : DOM.eventGetClientX(event);
+        return isFocused ? EVENT_XY_POSITION_OUTSIDE : DOM
+                .eventGetClientX(event);
     }
 
     private int getEventY(Event event, boolean isFocused) {
-        return isFocused ? -5000 : DOM.eventGetClientY(event);
+        return isFocused ? EVENT_XY_POSITION_OUTSIDE : DOM
+                .eventGetClientY(event);
     }
 
     @Override
diff --git a/uitest/src/com/vaadin/tests/components/TooltipPosition.java b/uitest/src/com/vaadin/tests/components/TooltipPosition.java
new file mode 100644 (file)
index 0000000..3022272
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * 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;
+
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.UI;
+import com.vaadin.ui.VerticalLayout;
+
+/**
+ * This UI is used for testing that a tooltip is not positioned partially
+ * outside the browser window when there is enough space to display it.
+ * 
+ * @author Vaadin Ltd
+ */
+public class TooltipPosition extends AbstractTestUI {
+
+    public static final int NUMBER_OF_BUTTONS = 5;
+
+    @Override
+    protected void setup(VaadinRequest request) {
+        // These tooltip delay settings can be removed once #13854 is resolved.
+        getTooltipConfiguration().setOpenDelay(0);
+        getTooltipConfiguration().setQuickOpenDelay(0);
+        getTooltipConfiguration().setCloseTimeout(1000);
+
+        VerticalLayout layout = new VerticalLayout();
+        layout.setSpacing(true);
+        layout.setHeight(UI.getCurrent().getPage().getBrowserWindowHeight(),
+                Unit.PIXELS);
+        addComponent(layout);
+        for (int i = 0; i < NUMBER_OF_BUTTONS; i++) {
+            Button button = new Button("Button");
+            button.setDescription(generateTooltipText());
+            layout.addComponent(button);
+        }
+    }
+
+    private String generateTooltipText() {
+        StringBuilder result = new StringBuilder();
+        for (int i = 0; i < 50; i++) {
+            result.append("This is the line ").append(i)
+                    .append(" of the long tooltip text.<br>");
+        }
+        return result.toString();
+    }
+
+    @Override
+    public String getTestDescription() {
+        return "The tooltips of the buttons should not be clipped when there is enough space to display them.";
+    }
+
+    @Override
+    public Integer getTicketNumber() {
+        return 15129;
+    }
+}
diff --git a/uitest/src/com/vaadin/tests/components/TooltipPositionTest.java b/uitest/src/com/vaadin/tests/components/TooltipPositionTest.java
new file mode 100644 (file)
index 0000000..4106374
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * 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;
+
+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.StaleElementReferenceException;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebDriver.Window;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.interactions.Actions;
+import org.openqa.selenium.support.ui.ExpectedCondition;
+
+import com.vaadin.testbench.elements.ButtonElement;
+import com.vaadin.tests.tb3.MultiBrowserTest;
+
+/**
+ * Tests that the tooltip is positioned so that it fits in the displayed area.
+ * 
+ * @author Vaadin Ltd
+ */
+public class TooltipPositionTest extends MultiBrowserTest {
+
+    @Test
+    public void testRegression_EmptyTooltipShouldNotBeAppearedDuringInitialization()
+            throws Exception {
+        openTestURL();
+
+        waitForElementVisible(By.cssSelector(".v-tooltip"));
+        WebElement tooltip = driver.findElement(By.cssSelector(".v-tooltip"));
+
+        Assert.assertTrue(
+                "This init tooltip with text ' ' is present in the DOM and should be entirely outside the browser window",
+                isOutsideOfWindow(tooltip));
+    }
+
+    @Test
+    public void testTooltipPosition() throws Exception {
+        openTestURL();
+        for (int i = 0; i < TooltipPosition.NUMBER_OF_BUTTONS; i++) {
+            ButtonElement button = $(ButtonElement.class).get(i);
+            // Move the mouse to display the tooltip.
+            Actions actions = new Actions(driver);
+            actions.moveToElement(button, 10, 10);
+            actions.build().perform();
+            waitUntil(tooltipToBeInsideWindow(By.cssSelector(".v-tooltip"),
+                    driver.manage().window()));
+
+            if (i < TooltipPosition.NUMBER_OF_BUTTONS - 1) {
+                // Remove the tooltip by moving the mouse.
+                actions = new Actions(driver);
+                actions.moveByOffset(300, 0);
+                actions.build().perform();
+                waitUntil(tooltipNotToBeShown(By.cssSelector(".v-tooltip"),
+                        driver.manage().window()));
+            }
+        }
+    }
+
+    /*
+     * An expectation for checking that the tooltip found by the given locator
+     * is present in the DOM and entirely inside the browser window. The
+     * coordinate of the top left corner of the window is supposed to be (0, 0).
+     */
+    private ExpectedCondition<Boolean> tooltipToBeInsideWindow(
+            final By tooltipLocator, final Window window) {
+        return new ExpectedCondition<Boolean>() {
+
+            @Override
+            public Boolean apply(WebDriver input) {
+                List<WebElement> elements = findElements(tooltipLocator);
+                if (elements.isEmpty()) {
+                    return false;
+                }
+                WebElement element = elements.get(0);
+                try {
+                    if (!element.isDisplayed()) {
+                        return false;
+                    }
+                    Point topLeft = element.getLocation();
+                    int xLeft = topLeft.getX();
+                    int yTop = topLeft.getY();
+                    if (xLeft < 0 || yTop < 0) {
+                        return false;
+                    }
+                    Dimension elementSize = element.getSize();
+                    int xRight = xLeft + elementSize.getWidth() - 1;
+                    int yBottom = yTop + elementSize.getHeight() - 1;
+                    Dimension browserSize = window.getSize();
+                    return xRight < browserSize.getWidth()
+                            && yBottom < browserSize.getHeight();
+                } catch (StaleElementReferenceException e) {
+                    return false;
+                }
+            }
+
+            @Override
+            public String toString() {
+                return "the tooltip to be displayed inside the window";
+            }
+        };
+    };
+
+    /*
+     * An expectation for checking that the tooltip found by the given locator
+     * is not shown in the window, even partially. The top left corner of window
+     * should have coordinates (0, 0).
+     */
+    private ExpectedCondition<Boolean> tooltipNotToBeShown(
+            final By tooltipLocator, final Window window) {
+        return new ExpectedCondition<Boolean>() {
+
+            @Override
+            public Boolean apply(WebDriver input) {
+                List<WebElement> elements = findElements(tooltipLocator);
+                if (elements.isEmpty()) {
+                    return true;
+                }
+                WebElement tooltip = elements.get(0);
+                try {
+                    return isOutsideOfWindow(tooltip);
+                } catch (StaleElementReferenceException e) {
+                    return true;
+                }
+            }
+
+            @Override
+            public String toString() {
+                return "the tooltip not to be displayed inside the window";
+            }
+
+        };
+    }
+
+    private boolean isOutsideOfWindow(WebElement tooltip) {
+        if (!tooltip.isDisplayed()) {
+            return true;
+        }
+        // The tooltip is shown, at least partially, if
+        // its intervals of both horizontal and vertical coordinates
+        // overlap those of the window.
+        Point topLeft = tooltip.getLocation();
+        Dimension tooltipSize = tooltip.getSize();
+        Dimension windowSize = driver.manage().window().getSize();
+        int xLeft = topLeft.getX();
+        int yTop = topLeft.getY();
+        int xRight = xLeft + tooltipSize.getWidth() - 1;
+        int yBottom = yTop + tooltipSize.getHeight() - 1;
+        boolean overlapHorizontally = !(xRight < 0 || xLeft >= windowSize
+                .getWidth());
+        boolean overlapVertically = !(yBottom < 0 || yTop >= windowSize
+                .getHeight());
+        return !(overlapHorizontally && overlapVertically);
+    }
+}
\ No newline at end of file
index 24025b9f3978f8248cf441eba9927501591a8ea0..091f7be95467d49831e4accd890bfb76249e533b 100644 (file)
@@ -49,6 +49,7 @@ public class MenuTooltipTest extends MultiBrowserTest {
 
         Coordinates elementCoordinates = getCoordinates($(MenuBarElement.class)
                 .first());
+        sleep(1000);
 
         Mouse mouse = ((HasInputDevices) getDriver()).getMouse();