diff options
author | Denis Anisimov <denis@vaadin.com> | 2014-11-06 09:15:15 +0200 |
---|---|---|
committer | Vaadin Code Review <review@vaadin.com> | 2014-12-10 08:18:00 +0000 |
commit | 8ce89592362226d6687e1267632a75d85774b67d (patch) | |
tree | 5fb85426e2f0b66f02aeeb2bd49e7d3570ab0669 | |
parent | f1f089772749a0eb4b031f1876b95bb684565473 (diff) | |
download | vaadin-framework-8ce89592362226d6687e1267632a75d85774b67d.tar.gz vaadin-framework-8ce89592362226d6687e1267632a75d85774b67d.zip |
Position calendar popup on the left side if there is no space (#14757).
Change-Id: I83836bbf077033712a569c8eff52576b012b4dee
8 files changed, 376 insertions, 54 deletions
diff --git a/WebContent/VAADIN/themes/valo/components/_datefield.scss b/WebContent/VAADIN/themes/valo/components/_datefield.scss index 3201288120..6d36ade43a 100644 --- a/WebContent/VAADIN/themes/valo/components/_datefield.scss +++ b/WebContent/VAADIN/themes/valo/components/_datefield.scss @@ -276,6 +276,8 @@ @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; diff --git a/client/src/com/vaadin/client/ui/VPopupCalendar.java b/client/src/com/vaadin/client/ui/VPopupCalendar.java index 51b2ee22ec..15302f0784 100644 --- a/client/src/com/vaadin/client/ui/VPopupCalendar.java +++ b/client/src/com/vaadin/client/ui/VPopupCalendar.java @@ -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 index 0000000000..4469ad3b1a --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/datefield/DateFieldPopupPosition.java @@ -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 index 0000000000..f896519aae --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/datefield/DateFieldPopupPositionTest.java @@ -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 index 0000000000..8e4de77837 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/datefield/DefaultDateFieldPopupPosition.java @@ -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 index 0000000000..61cc876a3f --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/datefield/DefaultDateFieldPopupPositionTest.java @@ -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 index 0000000000..59ff6aa9e8 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/datefield/ValoDateFieldPopupPosition.java @@ -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 index 0000000000..4009b9d991 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/datefield/ValoDateFieldPopupPositionTest.java @@ -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); + } +} |