Change-Id: I8563f1e2cfc66ca89399590401fd77ec67e50e82tags/7.3.0.beta1
import com.google.gwt.dom.client.Element; | import com.google.gwt.dom.client.Element; | ||||
import com.google.gwt.dom.client.Style; | import com.google.gwt.dom.client.Style; | ||||
import com.google.gwt.dom.client.Style.Display; | import com.google.gwt.dom.client.Style.Display; | ||||
import com.google.gwt.dom.client.Style.Overflow; | |||||
import com.google.gwt.dom.client.Style.Unit; | import com.google.gwt.dom.client.Style.Unit; | ||||
import com.google.gwt.event.dom.client.BlurEvent; | import com.google.gwt.event.dom.client.BlurEvent; | ||||
import com.google.gwt.event.dom.client.BlurHandler; | import com.google.gwt.event.dom.client.BlurHandler; | ||||
private final Element down = DOM.createDiv(); | private final Element down = DOM.createDiv(); | ||||
private final Element status = DOM.createDiv(); | private final Element status = DOM.createDiv(); | ||||
private int desiredHeight = -1; | |||||
private boolean isPagingEnabled = true; | private boolean isPagingEnabled = true; | ||||
private long lastAutoClosed; | private long lastAutoClosed; | ||||
debug("VFS.SP: constructor()"); | debug("VFS.SP: constructor()"); | ||||
setOwner(VFilterSelect.this); | setOwner(VFilterSelect.this); | ||||
menu = new SuggestionMenu(); | menu = new SuggestionMenu(); | ||||
menu.getElement().getStyle().setOverflowY(Overflow.AUTO); | |||||
setWidget(menu); | setWidget(menu); | ||||
getElement().getStyle().setZIndex(Z_INDEX); | getElement().getStyle().setZIndex(Z_INDEX); | ||||
public void setPosition(int offsetWidth, int offsetHeight) { | public void setPosition(int offsetWidth, int offsetHeight) { | ||||
debug("VFS.SP: setPosition()"); | debug("VFS.SP: setPosition()"); | ||||
int top = -1; | |||||
int left = -1; | |||||
int top = getPopupTop(); | |||||
int left = getPopupLeft(); | |||||
// reset menu size and retrieve its "natural" size | |||||
menu.setHeight(""); | |||||
if (currentPage > 0) { | |||||
// fix height to avoid height change when getting to last page | |||||
menu.fixHeightTo(pageLength); | |||||
if (desiredHeight < 0) { | |||||
desiredHeight = offsetHeight; | |||||
} | } | ||||
offsetHeight = getOffsetHeight(); | |||||
final int desiredWidth = getMainWidth(); | final int desiredWidth = getMainWidth(); | ||||
Element menuFirstChild = menu.getElement().getFirstChildElement(); | Element menuFirstChild = menu.getElement().getFirstChildElement(); | ||||
getContainerElement().getStyle().setWidth(rootWidth, Unit.PX); | getContainerElement().getStyle().setWidth(rootWidth, Unit.PX); | ||||
} | } | ||||
if (offsetHeight + getPopupTop() > Window.getClientHeight() | |||||
+ Window.getScrollTop()) { | |||||
final int spaceAvailableBelow = Window.getClientHeight() | |||||
- (top - Window.getScrollTop()); | |||||
final int spaceAvailableAbove = top - Window.getScrollTop() | |||||
- VFilterSelect.this.getOffsetHeight(); | |||||
if (spaceAvailableBelow < desiredHeight | |||||
&& spaceAvailableBelow < spaceAvailableAbove) { | |||||
// popup on top of input instead | // popup on top of input instead | ||||
top = getPopupTop() - offsetHeight | |||||
- VFilterSelect.this.getOffsetHeight(); | |||||
top -= desiredHeight + VFilterSelect.this.getOffsetHeight(); | |||||
offsetHeight = desiredHeight; | |||||
if (top < 0) { | if (top < 0) { | ||||
offsetHeight += top; | |||||
top = 0; | top = 0; | ||||
} | } | ||||
} else { | } else { | ||||
top = getPopupTop(); | |||||
/* | /* | ||||
* Take popup top margin into account. getPopupTop() returns the | * Take popup top margin into account. getPopupTop() returns the | ||||
* top value including the margin but the value we give must not | * top value including the margin but the value we give must not | ||||
*/ | */ | ||||
int topMargin = (top - topPosition); | int topMargin = (top - topPosition); | ||||
top -= topMargin; | top -= topMargin; | ||||
offsetHeight = Math.min(desiredHeight, spaceAvailableBelow); | |||||
} | |||||
/* | |||||
* Resize popup and menu if calculated height doesn't match the | |||||
* actual height | |||||
*/ | |||||
if (getOffsetHeight() != offsetHeight) { | |||||
int menuHeight = offsetHeight - up.getOffsetHeight() | |||||
- down.getOffsetHeight() - status.getOffsetHeight(); | |||||
menu.setHeight(menuHeight + "px"); | |||||
getContainerElement().getStyle().setHeight(offsetHeight, | |||||
Unit.PX); | |||||
} | } | ||||
// fetch real width (mac FF bugs here due GWT popups overflow:auto ) | // fetch real width (mac FF bugs here due GWT popups overflow:auto ) | ||||
if (left < 0) { | if (left < 0) { | ||||
left = 0; | left = 0; | ||||
} | } | ||||
} else { | |||||
left = getPopupLeft(); | |||||
} | } | ||||
setPopupPosition(left, top); | setPopupPosition(left, top); | ||||
} | } |
/* | |||||
* 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.combobox; | |||||
import com.vaadin.server.VaadinRequest; | |||||
import com.vaadin.tests.components.AbstractTestUI; | |||||
import com.vaadin.ui.Alignment; | |||||
import com.vaadin.ui.ComboBox; | |||||
import com.vaadin.ui.VerticalLayout; | |||||
/** | |||||
* Test UI for issue #11929 where ComboBox suggestion popup hides the ComboBox | |||||
* itself obscuring the text input field. | |||||
* | |||||
* @author Vaadin Ltd | |||||
*/ | |||||
public class ComboBoxOnSmallScreen extends AbstractTestUI { | |||||
private static final String PID = "captionPID"; | |||||
@Override | |||||
protected void setup(VaadinRequest request) { | |||||
addComponents(createComboBox(), createComboBox()); | |||||
VerticalLayout vl = getLayout(); | |||||
vl.setHeight(300, Unit.PIXELS); | |||||
vl.setComponentAlignment(vl.getComponent(1), Alignment.BOTTOM_LEFT); | |||||
} | |||||
@Override | |||||
protected String getTestDescription() { | |||||
return "Combobox hides what you are typing on small screen"; | |||||
} | |||||
@Override | |||||
protected Integer getTicketNumber() { | |||||
return 11929; | |||||
} | |||||
private ComboBox createComboBox() { | |||||
ComboBox cb = new ComboBox(); | |||||
cb.addContainerProperty(PID, String.class, ""); | |||||
cb.setItemCaptionPropertyId(PID); | |||||
for (int i = 1; i < 21; ++i) { | |||||
final String v = "Item #" + i; | |||||
cb.getItem(cb.addItem()).getItemProperty(PID).setValue(v); | |||||
} | |||||
return cb; | |||||
} | |||||
} |
/* | |||||
* 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.combobox; | |||||
import static org.hamcrest.MatcherAssert.assertThat; | |||||
import static org.hamcrest.Matchers.is; | |||||
import org.junit.Test; | |||||
import org.openqa.selenium.By; | |||||
import org.openqa.selenium.Dimension; | |||||
import org.openqa.selenium.WebDriver.Window; | |||||
import org.openqa.selenium.WebElement; | |||||
import com.vaadin.client.ui.VFilterSelect; | |||||
import com.vaadin.testbench.elements.ComboBoxElement; | |||||
import com.vaadin.tests.tb3.MultiBrowserTest; | |||||
/** | |||||
* ComboBox suggestion popup should not obscure the text input box. | |||||
* | |||||
* @author Vaadin Ltd | |||||
*/ | |||||
public class ComboBoxOnSmallScreenTest extends MultiBrowserTest { | |||||
private static final Dimension TARGETSIZE = new Dimension(600, 300); | |||||
private static final String POPUPCLASSNAME = VFilterSelect.CLASSNAME | |||||
+ "-suggestpopup"; | |||||
@Override | |||||
public void setup() throws Exception { | |||||
super.setup(); | |||||
openTestURL(); | |||||
getWindow().setSize(TARGETSIZE); | |||||
} | |||||
@Test | |||||
public void testUpperSuggestionPopupOverlayPosition() { | |||||
ComboBoxElement cb = getComboBoxAndOpenPopup(0); | |||||
assertOverlayPosition(cb, getPopup()); | |||||
} | |||||
@Test | |||||
public void testUpperSuggestionPopupOverlaySize() { | |||||
ComboBoxElement cb = getComboBoxAndOpenPopup(0); | |||||
assertOverlaySize(cb, getPopup()); | |||||
} | |||||
@Test | |||||
public void testLowerSuggestionPopupOverlayPosition() { | |||||
ComboBoxElement cb = getComboBoxAndOpenPopup(1); | |||||
assertOverlayPosition(cb, getPopup()); | |||||
} | |||||
@Test | |||||
public void testLowerSuggestionPopupOverlaySize() { | |||||
ComboBoxElement cb = getComboBoxAndOpenPopup(1); | |||||
assertOverlaySize(cb, getPopup()); | |||||
} | |||||
private void assertOverlayPosition(WebElement combobox, WebElement popup) { | |||||
final int popupTop = popup.getLocation().y; | |||||
final int popupBottom = popupTop + popup.getSize().getHeight(); | |||||
final int cbTop = combobox.getLocation().y; | |||||
final int cbBottom = cbTop + combobox.getSize().getHeight(); | |||||
assertThat("Popup overlay does not overlap with the textbox", | |||||
popupTop >= cbBottom || popupBottom <= cbTop, is(true)); | |||||
} | |||||
private void assertOverlaySize(WebElement combobox, WebElement popup) { | |||||
final int popupTop = popup.getLocation().y; | |||||
final int popupBottom = popupTop + popup.getSize().getHeight(); | |||||
final int rootHeight = findElement(By.tagName("body")).getSize().height; | |||||
assertThat("Popup overlay inside the viewport", popupTop < 0 | |||||
|| popupBottom > rootHeight, is(false)); | |||||
} | |||||
private ComboBoxElement getComboBoxAndOpenPopup(int comboboxIndex) { | |||||
ComboBoxElement cb = $(ComboBoxElement.class).get(comboboxIndex); | |||||
cb.openPopup(); | |||||
return cb; | |||||
} | |||||
private WebElement getPopup() { | |||||
return findElement(By.className(POPUPCLASSNAME)); | |||||
} | |||||
private Window getWindow() { | |||||
return getDriver().manage().window(); | |||||
} | |||||
} |