]> source.dussan.org Git - vaadin-framework.git/commitdiff
Position calendar popup on the left side if there is no space (#14757).
authorDenis Anisimov <denis@vaadin.com>
Thu, 6 Nov 2014 07:15:15 +0000 (09:15 +0200)
committerVaadin Code Review <review@vaadin.com>
Wed, 10 Dec 2014 08:18:00 +0000 (08:18 +0000)
Change-Id: I83836bbf077033712a569c8eff52576b012b4dee

WebContent/VAADIN/themes/valo/components/_datefield.scss
client/src/com/vaadin/client/ui/VPopupCalendar.java
uitest/src/com/vaadin/tests/components/datefield/DateFieldPopupPosition.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/datefield/DateFieldPopupPositionTest.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/datefield/DefaultDateFieldPopupPosition.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/datefield/DefaultDateFieldPopupPositionTest.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/datefield/ValoDateFieldPopupPosition.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/components/datefield/ValoDateFieldPopupPositionTest.java [new file with mode: 0644]

index 32012881206c304e5f08467169bae19d012b3676..6d36ade43a9e2ef47206446f72d9e4f4d94ade4b 100644 (file)
   @include valo-overlay-style;
 
   margin-top: ceil($v-unit-size/8) !important;
+  margin-bottom: ceil($v-unit-size/8) !important;
+  margin-right: ceil($v-unit-size/8) !important;
   cursor: default;
   width: auto;
 
index 51b2ee22ece774c229f7631ae4b8757789780524..15302f078448ee5c7a715f69ef72a3666ff6ef9f 100644 (file)
@@ -42,6 +42,7 @@ import com.google.gwt.user.client.ui.PopupPanel.PositionCallback;
 import com.google.gwt.user.client.ui.RootPanel;
 import com.google.gwt.user.client.ui.Widget;
 import com.vaadin.client.BrowserInfo;
+import com.vaadin.client.ComputedStyle;
 import com.vaadin.client.VConsole;
 import com.vaadin.client.ui.VCalendarPanel.FocusOutListener;
 import com.vaadin.client.ui.VCalendarPanel.SubmitListener;
@@ -372,60 +373,7 @@ public class VPopupCalendar extends VTextualDate implements Field,
             // clear previous values
             popup.setWidth("");
             popup.setHeight("");
-            popup.setPopupPositionAndShow(new PositionCallback() {
-                @Override
-                public void setPosition(int offsetWidth, int offsetHeight) {
-                    final int w = offsetWidth;
-                    final int h = offsetHeight;
-                    final int browserWindowWidth = Window.getClientWidth()
-                            + Window.getScrollLeft();
-                    final int browserWindowHeight = Window.getClientHeight()
-                            + Window.getScrollTop();
-                    int t = calendarToggle.getAbsoluteTop();
-                    int l = calendarToggle.getAbsoluteLeft();
-
-                    // Add a little extra space to the right to avoid
-                    // problems with IE7 scrollbars and to make it look
-                    // nicer.
-                    int extraSpace = 30;
-
-                    boolean overflowRight = false;
-                    if (l + +w + extraSpace > browserWindowWidth) {
-                        overflowRight = true;
-                        // Part of the popup is outside the browser window
-                        // (to the right)
-                        l = browserWindowWidth - w - extraSpace;
-                    }
-
-                    if (t + h + calendarToggle.getOffsetHeight() + 30 > browserWindowHeight) {
-                        // Part of the popup is outside the browser window
-                        // (below)
-                        t = browserWindowHeight - h
-                                - calendarToggle.getOffsetHeight() - 30;
-                        if (!overflowRight) {
-                            // Show to the right of the popup button unless we
-                            // are in the lower right corner of the screen
-                            l += calendarToggle.getOffsetWidth();
-                        }
-                    }
-
-                    popup.setPopupPosition(l,
-                            t + calendarToggle.getOffsetHeight() + 2);
-
-                    /*
-                     * We have to wait a while before focusing since the popup
-                     * needs to be opened before we can focus
-                     */
-                    Timer focusTimer = new Timer() {
-                        @Override
-                        public void run() {
-                            setFocus(true);
-                        }
-                    };
-
-                    focusTimer.schedule(100);
-                }
-            });
+            popup.setPopupPositionAndShow(new PopupPositionCallback());
         } else {
             VConsole.error("Cannot reopen popup, it is already open!");
         }
@@ -642,4 +590,113 @@ public class VPopupCalendar extends VTextualDate implements Field,
         calendar.setRangeEnd(rangeEnd);
     }
 
+    private class PopupPositionCallback implements PositionCallback {
+
+        @Override
+        public void setPosition(int offsetWidth, int offsetHeight) {
+            final int width = offsetWidth;
+            final int height = offsetHeight;
+            final int browserWindowWidth = Window.getClientWidth()
+                    + Window.getScrollLeft();
+            final int windowHeight = Window.getClientHeight()
+                    + Window.getScrollTop();
+            int left = calendarToggle.getAbsoluteLeft();
+
+            // Add a little extra space to the right to avoid
+            // problems with IE7 scrollbars and to make it look
+            // nicer.
+            int extraSpace = 30;
+
+            boolean overflow = left + width + extraSpace > browserWindowWidth;
+            if (overflow) {
+                // Part of the popup is outside the browser window
+                // (to the right)
+                left = browserWindowWidth - width - extraSpace;
+            }
+
+            int top = calendarToggle.getAbsoluteTop();
+            int extraHeight = 2;
+            boolean verticallyRepositioned = false;
+            ComputedStyle style = new ComputedStyle(popup.getElement());
+            int[] margins = style.getMargin();
+            int desiredPopupBottom = top + height
+                    + calendarToggle.getOffsetHeight() + margins[0]
+                    + margins[2];
+
+            if (desiredPopupBottom > windowHeight) {
+                int updatedLeft = left;
+                left = getLeftPosition(left, width, style, overflow);
+
+                // if position has not been changed then it means there is no
+                // space to make popup fully visible
+                if (updatedLeft == left) {
+                    // let's try to show popup on the top of the field
+                    int updatedTop = top - extraHeight - height - margins[0]
+                            - margins[2];
+                    verticallyRepositioned = updatedTop >= 0;
+                    if (verticallyRepositioned) {
+                        top = updatedTop;
+                    }
+                }
+                // Part of the popup is outside the browser window
+                // (below)
+                if (!verticallyRepositioned) {
+                    verticallyRepositioned = true;
+                    top = windowHeight - height - extraSpace + extraHeight;
+                }
+            }
+            if (verticallyRepositioned) {
+                popup.setPopupPosition(left, top);
+            } else {
+                popup.setPopupPosition(left,
+                        top + calendarToggle.getOffsetHeight() + extraHeight);
+            }
+            doSetFocus();
+        }
+
+        private int getLeftPosition(int left, int width, ComputedStyle style,
+                boolean overflow) {
+            if (positionRightSide()) {
+                // Show to the right of the popup button unless we
+                // are in the lower right corner of the screen
+                if (overflow) {
+                    return left;
+                } else {
+                    return left + calendarToggle.getOffsetWidth();
+                }
+            } else {
+                int[] margins = style.getMargin();
+                int desiredLeftPosition = calendarToggle.getAbsoluteLeft()
+                        - width - margins[1] - margins[3];
+                if (desiredLeftPosition >= 0) {
+                    return desiredLeftPosition;
+                } else {
+                    return left;
+                }
+            }
+        }
+
+        private boolean positionRightSide() {
+            int buttonRightSide = calendarToggle.getAbsoluteLeft()
+                    + calendarToggle.getOffsetWidth();
+            int textRightSide = text.getAbsoluteLeft() + text.getOffsetWidth();
+            return buttonRightSide >= textRightSide;
+        }
+
+        private void doSetFocus() {
+            /*
+             * We have to wait a while before focusing since the popup needs to
+             * be opened before we can focus
+             */
+            Timer focusTimer = new Timer() {
+                @Override
+                public void run() {
+                    setFocus(true);
+                }
+            };
+
+            focusTimer.schedule(100);
+        }
+    }
+
 }
diff --git a/uitest/src/com/vaadin/tests/components/datefield/DateFieldPopupPosition.java b/uitest/src/com/vaadin/tests/components/datefield/DateFieldPopupPosition.java
new file mode 100644 (file)
index 0000000..4469ad3
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * 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.datefield;
+
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.tests.components.AbstractTestUI;
+import com.vaadin.ui.HorizontalLayout;
+import com.vaadin.ui.Label;
+import com.vaadin.ui.PopupDateField;
+
+/**
+ * Test UI for date field Popup calendar.
+ * 
+ * @author Vaadin Ltd
+ */
+public abstract class DateFieldPopupPosition extends AbstractTestUI {
+
+    @Override
+    protected void setup(VaadinRequest request) {
+        HorizontalLayout layout = new HorizontalLayout();
+        addComponent(layout);
+        Label gap = new Label();
+        gap.setWidth(250, Unit.PIXELS);
+        layout.addComponent(gap);
+        PopupDateField field = new PopupDateField();
+        layout.addComponent(field);
+    }
+
+    @Override
+    protected Integer getTicketNumber() {
+        return 14757;
+    }
+
+    @Override
+    protected String getTestDescription() {
+        return "Calendar popup should not placed on the top of text field when "
+                + "there is no space on bottom.";
+    }
+}
diff --git a/uitest/src/com/vaadin/tests/components/datefield/DateFieldPopupPositionTest.java b/uitest/src/com/vaadin/tests/components/datefield/DateFieldPopupPositionTest.java
new file mode 100644 (file)
index 0000000..f896519
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * 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.datefield;
+
+import org.junit.Test;
+import org.openqa.selenium.By;
+import org.openqa.selenium.Dimension;
+import org.openqa.selenium.WebElement;
+
+import com.vaadin.testbench.elements.DateFieldElement;
+import com.vaadin.tests.tb3.MultiBrowserTest;
+
+/**
+ * Test for date field popup calendar position.
+ * 
+ * @author Vaadin Ltd
+ */
+public abstract class DateFieldPopupPositionTest extends MultiBrowserTest {
+
+    @Test
+    public void testPopupPosition() {
+        openTestURL();
+
+        int height = getFieldBottom() + 150;
+        adjustBrowserWindow(height);
+
+        openPopup();
+
+        checkPopupPosition();
+    }
+
+    protected abstract void checkPopupPosition();
+
+    protected WebElement getPopup() {
+        return findElement(By.className("v-datefield-popup"));
+    }
+
+    private void adjustBrowserWindow(int height) {
+        Dimension size = getDriver().manage().window().getSize();
+        getDriver().manage().window()
+                .setSize(new Dimension(size.getWidth(), height));
+    }
+
+    private int getFieldBottom() {
+        DateFieldElement dateField = $(DateFieldElement.class).first();
+        return dateField.getLocation().getY() + dateField.getSize().getHeight();
+    }
+
+    private void openPopup() {
+        findElement(By.className("v-datefield-button")).click();
+        if (!isElementPresent(By.className("v-datefield-popup"))) {
+            findElement(By.className("v-datefield-button")).click();
+        }
+    }
+}
diff --git a/uitest/src/com/vaadin/tests/components/datefield/DefaultDateFieldPopupPosition.java b/uitest/src/com/vaadin/tests/components/datefield/DefaultDateFieldPopupPosition.java
new file mode 100644 (file)
index 0000000..8e4de77
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * 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.datefield;
+
+/**
+ * Test UI for date field Popup calendar in default theme.
+ * 
+ * All UI initialization is defined in super class.
+ * 
+ * @author Vaadin Ltd
+ */
+public class DefaultDateFieldPopupPosition extends DateFieldPopupPosition {
+
+}
diff --git a/uitest/src/com/vaadin/tests/components/datefield/DefaultDateFieldPopupPositionTest.java b/uitest/src/com/vaadin/tests/components/datefield/DefaultDateFieldPopupPositionTest.java
new file mode 100644 (file)
index 0000000..61cc876
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * 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.datefield;
+
+import org.junit.Assert;
+import org.openqa.selenium.WebElement;
+
+import com.vaadin.testbench.elements.DateFieldElement;
+
+/**
+ * Test for date field popup calendar position in default theme.
+ * 
+ * Test method is defined in super class.
+ * 
+ * @author Vaadin Ltd
+ */
+public class DefaultDateFieldPopupPositionTest extends
+        DateFieldPopupPositionTest {
+
+    @Override
+    protected void checkPopupPosition() {
+        DateFieldElement field = $(DateFieldElement.class).first();
+        int right = field.getLocation().getX() + field.getSize().getWidth();
+        WebElement popup = getPopup();
+
+        Assert.assertTrue("Calendar popup has wrong X coordinate="
+                + popup.getLocation().getX() + " , right side of the field is "
+                + right, popup.getLocation().getX() >= right);
+    }
+}
diff --git a/uitest/src/com/vaadin/tests/components/datefield/ValoDateFieldPopupPosition.java b/uitest/src/com/vaadin/tests/components/datefield/ValoDateFieldPopupPosition.java
new file mode 100644 (file)
index 0000000..59ff6aa
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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.datefield;
+
+import com.vaadin.annotations.Theme;
+
+/**
+ * Test UI for date field Popup calendar in Valo theme.
+ * 
+ * All UI initialization is defined in super class.
+ * 
+ * @author Vaadin Ltd
+ */
+@Theme("valo")
+public class ValoDateFieldPopupPosition extends DateFieldPopupPosition {
+
+}
diff --git a/uitest/src/com/vaadin/tests/components/datefield/ValoDateFieldPopupPositionTest.java b/uitest/src/com/vaadin/tests/components/datefield/ValoDateFieldPopupPositionTest.java
new file mode 100644 (file)
index 0000000..4009b9d
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * 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.datefield;
+
+import org.junit.Assert;
+import org.openqa.selenium.WebElement;
+
+import com.vaadin.testbench.elements.DateFieldElement;
+
+/**
+ * Test for date field popup calendar position in Valo theme.
+ * 
+ * Test method is defined in super class.
+ * 
+ * @author Vaadin Ltd
+ */
+public class ValoDateFieldPopupPositionTest extends DateFieldPopupPositionTest {
+
+    @Override
+    protected void checkPopupPosition() {
+        DateFieldElement field = $(DateFieldElement.class).first();
+        WebElement popup = getPopup();
+        int left = field.getLocation().getX();
+        int popupRight = popup.getLocation().getX()
+                + popup.getSize().getWidth();
+
+        Assert.assertTrue("Calendar popup has wrong X coordinate=" + popupRight
+                + " , left side of the field is " + left, popupRight <= left);
+    }
+}