From 4af5c386282e476e95ae54d239a57f22ae0ba205 Mon Sep 17 00:00:00 2001 From: Jonni Nakari Date: Fri, 28 Nov 2014 14:47:29 +0200 Subject: Comment typo fix Change-Id: I3f01eb1725b81c834f990f9ea2e69d4dfe420993 --- client/src/com/vaadin/client/ui/textfield/TextFieldConnector.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/com/vaadin/client/ui/textfield/TextFieldConnector.java b/client/src/com/vaadin/client/ui/textfield/TextFieldConnector.java index 42b045005f..1a4b64b0a6 100644 --- a/client/src/com/vaadin/client/ui/textfield/TextFieldConnector.java +++ b/client/src/com/vaadin/client/ui/textfield/TextFieldConnector.java @@ -84,7 +84,7 @@ public class TextFieldConnector extends AbstractFieldConnector implements /* * We skip the text content update if field has been repainted, but text * has not been changed. Additional sanity check verifies there is no - * change in the que (in which case we count more on the server side + * change in the queue (in which case we count more on the server side * value). */ if (!(uidl -- cgit v1.2.3 From f301dd8d759c5006cb411df0e424a876e04b84fb Mon Sep 17 00:00:00 2001 From: Fabian Lange Date: Mon, 10 Nov 2014 19:07:12 +0100 Subject: set Cache-Control and Expires header even when not-modified (#8757) Usually first a resource is served with the lower code block. this provides cache-control, expires and last-modification headers to the browser. But when a not-modified response was served, these headers were missing. This effectively caused the caching to no longer work once the not-modified responses are sent out. Change-Id: I9b1f0cacc91734f88bb0384872da0d426d4b5fe0 --- server/src/com/vaadin/server/VaadinServlet.java | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/server/src/com/vaadin/server/VaadinServlet.java b/server/src/com/vaadin/server/VaadinServlet.java index 4656f9a672..4fd1e97a40 100644 --- a/server/src/com/vaadin/server/VaadinServlet.java +++ b/server/src/com/vaadin/server/VaadinServlet.java @@ -704,6 +704,15 @@ public class VaadinServlet extends HttpServlet implements Constants { return; } + String cacheControl = "public, max-age=0, must-revalidate"; + int resourceCacheTime = getCacheTime(filename); + if (resourceCacheTime > 0) { + cacheControl = "max-age=" + String.valueOf(resourceCacheTime); + } + response.setHeader("Cache-Control", cacheControl); + response.setDateHeader("Expires", System.currentTimeMillis() + + (resourceCacheTime * 1000)); + // Find the modification timestamp long lastModifiedTime = 0; URLConnection connection = null; @@ -714,6 +723,7 @@ public class VaadinServlet extends HttpServlet implements Constants { // are not returned by the browser in the "If-Modified-Since" // header). lastModifiedTime = lastModifiedTime - lastModifiedTime % 1000; + response.setDateHeader("Last-Modified", lastModifiedTime); if (browserHasNewestVersion(request, lastModifiedTime)) { response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); @@ -748,18 +758,6 @@ public class VaadinServlet extends HttpServlet implements Constants { response.setContentType(mimetype); } - // Provide modification timestamp to the browser if it is known. - if (lastModifiedTime > 0) { - response.setDateHeader("Last-Modified", lastModifiedTime); - - String cacheControl = "public, max-age=0, must-revalidate"; - int resourceCacheTime = getCacheTime(filename); - if (resourceCacheTime > 0) { - cacheControl = "max-age=" + String.valueOf(resourceCacheTime); - } - response.setHeader("Cache-Control", cacheControl); - } - writeStaticResourceResponse(request, response, resourceUrl); } -- cgit v1.2.3 From 58cf6113d02cb0331dae6d5ff46eb3e4a0ccdb01 Mon Sep 17 00:00:00 2001 From: Denis Anisimov Date: Wed, 5 Nov 2014 19:37:31 +0200 Subject: Prevent NPE in AbstractBeanContainer.getType() method (#15173). Change-Id: Ieaed329ed85c68d0da8bd169b4cc5c5886dde212 --- server/src/com/vaadin/data/util/AbstractBeanContainer.java | 8 ++++++-- .../tests/src/com/vaadin/data/util/BeanContainerTest.java | 13 +++++++++++++ .../src/com/vaadin/data/util/BeanItemContainerTest.java | 13 +++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/server/src/com/vaadin/data/util/AbstractBeanContainer.java b/server/src/com/vaadin/data/util/AbstractBeanContainer.java index adf6313770..7d7d71c423 100644 --- a/server/src/com/vaadin/data/util/AbstractBeanContainer.java +++ b/server/src/com/vaadin/data/util/AbstractBeanContainer.java @@ -152,7 +152,7 @@ public abstract class AbstractBeanContainer extends * A description of the properties found in beans of type {@link #type}. * Determines the property ids that are present in the container. */ - private LinkedHashMap> model; + private final LinkedHashMap> model; /** * Constructs a {@code AbstractBeanContainer} for beans of the given type. @@ -178,7 +178,11 @@ public abstract class AbstractBeanContainer extends */ @Override public Class getType(Object propertyId) { - return model.get(propertyId).getPropertyType(); + VaadinPropertyDescriptor descriptor = model.get(propertyId); + if (descriptor == null) { + return null; + } + return descriptor.getPropertyType(); } /** diff --git a/server/tests/src/com/vaadin/data/util/BeanContainerTest.java b/server/tests/src/com/vaadin/data/util/BeanContainerTest.java index 4e0e98ec43..f22ab8478e 100644 --- a/server/tests/src/com/vaadin/data/util/BeanContainerTest.java +++ b/server/tests/src/com/vaadin/data/util/BeanContainerTest.java @@ -68,6 +68,19 @@ public class BeanContainerTest extends AbstractBeanContainerTest { return false; } + public void testGetType_existingProperty_typeReturned() { + BeanContainer container = getContainer(); + Assert.assertEquals( + "Unexpected type is returned for property 'simpleName'", + String.class, container.getType("simpleName")); + } + + public void testGetType_notExistingProperty_nullReturned() { + BeanContainer container = getContainer(); + Assert.assertNull("Not null type is returned for property ''", + container.getType("")); + } + public void testBasicOperations() { testBasicContainerOperations(getContainer()); } diff --git a/server/tests/src/com/vaadin/data/util/BeanItemContainerTest.java b/server/tests/src/com/vaadin/data/util/BeanItemContainerTest.java index b58d962d96..01c282e294 100644 --- a/server/tests/src/com/vaadin/data/util/BeanItemContainerTest.java +++ b/server/tests/src/com/vaadin/data/util/BeanItemContainerTest.java @@ -71,6 +71,19 @@ public class BeanItemContainerTest extends AbstractBeanContainerTest { return false; } + public void testGetType_existingProperty_typeReturned() { + BeanItemContainer container = getContainer(); + Assert.assertEquals( + "Unexpected type is returned for property 'simpleName'", + String.class, container.getType("simpleName")); + } + + public void testGetType_notExistingProperty_nullReturned() { + BeanItemContainer container = getContainer(); + Assert.assertNull("Not null type is returned for property ''", + container.getType("")); + } + public void testBasicOperations() { testBasicContainerOperations(getContainer()); } -- cgit v1.2.3 From 53f87e5bf3014382f0bf9cd7acb18c56709ba1f7 Mon Sep 17 00:00:00 2001 From: Denis Anisimov Date: Sun, 19 Oct 2014 14:54:47 +0300 Subject: Show push version information in debug window (#14904). Change-Id: Id1761abbf2b2dc29b4138520f11ce51bb4d423fc --- .../vaadin/client/ApplicationConfiguration.java | 45 +++++++++ .../vaadin/client/debug/internal/InfoSection.java | 8 ++ server/src/com/vaadin/server/BootstrapHandler.java | 2 + .../com/vaadin/tests/debug/PushVersionInfo.java | 51 +++++++++++ .../vaadin/tests/debug/PushVersionInfoTest.java | 102 +++++++++++++++++++++ 5 files changed, 208 insertions(+) create mode 100644 uitest/src/com/vaadin/tests/debug/PushVersionInfo.java create mode 100644 uitest/src/com/vaadin/tests/debug/PushVersionInfoTest.java diff --git a/client/src/com/vaadin/client/ApplicationConfiguration.java b/client/src/com/vaadin/client/ApplicationConfiguration.java index 12b1585292..4865e38a4a 100644 --- a/client/src/com/vaadin/client/ApplicationConfiguration.java +++ b/client/src/com/vaadin/client/ApplicationConfiguration.java @@ -172,6 +172,33 @@ public class ApplicationConfiguration implements EntryPoint { return this.getConfig("versionInfo").vaadinVersion; }-*/; + /** + * Gets the version of the Atmosphere framework. + * + * @return a string with the version + * + * @see org.atmosphere.util#getRawVersion() + */ + private native String getAtmosphereVersion() + /*-{ + return this.getConfig("versionInfo").atmosphereVersion; + }-*/; + + /** + * Gets the JS version used in the Atmosphere framework. + * + * @return a string with the version + */ + private native String getAtmosphereJSVersion() + /*-{ + if ($wnd.jQueryVaadin != undefined){ + return $wnd.jQueryVaadin.atmosphere.version; + } + else { + return null; + } + }-*/; + private native String getUIDL() /*-{ return this.getConfig("uidl"); @@ -461,6 +488,24 @@ public class ApplicationConfiguration implements EntryPoint { return getJsoConfiguration(id).getVaadinVersion(); } + /** + * Return Atmosphere version. + * + * @return Atmosphere version. + */ + public String getAtmosphereVersion() { + return getJsoConfiguration(id).getAtmosphereVersion(); + } + + /** + * Return Atmosphere JS version. + * + * @return Atmosphere JS version. + */ + public String getAtmosphereJSVersion() { + return getJsoConfiguration(id).getAtmosphereJSVersion(); + } + public Class getConnectorClassByEncodedTag( int tag) { Class type = classes.get(tag); diff --git a/client/src/com/vaadin/client/debug/internal/InfoSection.java b/client/src/com/vaadin/client/debug/internal/InfoSection.java index a7a84f5f8f..dfb31cdd18 100644 --- a/client/src/com/vaadin/client/debug/internal/InfoSection.java +++ b/client/src/com/vaadin/client/debug/internal/InfoSection.java @@ -193,6 +193,9 @@ public class InfoSection implements Section { ApplicationConfiguration applicationConfiguration) { String clientVersion = Version.getFullVersion(); String servletVersion = applicationConfiguration.getServletVersion(); + String atmosphereVersion = applicationConfiguration + .getAtmosphereVersion(); + String jsVersion = applicationConfiguration.getAtmosphereJSVersion(); String themeVersion; boolean themeOk; @@ -213,6 +216,11 @@ public class InfoSection implements Section { addRow("Server engine version", servletVersion, servletOk ? null : ERROR_STYLE); addRow("Theme version", themeVersion, themeOk ? null : ERROR_STYLE); + if (jsVersion != null) { + addRow("Push server version", atmosphereVersion); + addRow("Push client version", jsVersion + + " (note: does not need to match server version)"); + } } /** diff --git a/server/src/com/vaadin/server/BootstrapHandler.java b/server/src/com/vaadin/server/BootstrapHandler.java index f0666f63fc..c34e986fce 100644 --- a/server/src/com/vaadin/server/BootstrapHandler.java +++ b/server/src/com/vaadin/server/BootstrapHandler.java @@ -469,6 +469,8 @@ public abstract class BootstrapHandler extends SynchronizedRequestHandler { JsonObject versionInfo = Json.createObject(); versionInfo.put("vaadinVersion", Version.getFullVersion()); + versionInfo.put("atmosphereVersion", + org.atmosphere.util.Version.getRawVersion()); appConfig.put("versionInfo", versionInfo); appConfig.put("widgetset", context.getWidgetsetName()); diff --git a/uitest/src/com/vaadin/tests/debug/PushVersionInfo.java b/uitest/src/com/vaadin/tests/debug/PushVersionInfo.java new file mode 100644 index 0000000000..d8c23a390f --- /dev/null +++ b/uitest/src/com/vaadin/tests/debug/PushVersionInfo.java @@ -0,0 +1,51 @@ +/* + * 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.debug; + +import org.atmosphere.util.Version; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.shared.communication.PushMode; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Label; + +/** + * Test UI for PUSH version string in debug window. + * + * @author Vaadin Ltd + */ +public class PushVersionInfo extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + if (request.getParameter("enablePush") != null) { + getPushConfiguration().setPushMode(PushMode.AUTOMATIC); + Label label = new Label(Version.getRawVersion()); + label.addStyleName("atmosphere-version"); + addComponent(label); + } + } + + @Override + public String getDescription() { + return "Debug window shows Push version in info Tab."; + } + + @Override + protected Integer getTicketNumber() { + return 14904; + } +} diff --git a/uitest/src/com/vaadin/tests/debug/PushVersionInfoTest.java b/uitest/src/com/vaadin/tests/debug/PushVersionInfoTest.java new file mode 100644 index 0000000000..90ea645ab8 --- /dev/null +++ b/uitest/src/com/vaadin/tests/debug/PushVersionInfoTest.java @@ -0,0 +1,102 @@ +/* + * 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.debug; + +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.Keys; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.interactions.Actions; + +import com.vaadin.tests.annotations.TestCategory; +import com.vaadin.tests.tb3.MultiBrowserTest; + +/** + * Test for PUSH version string in debug window. + * + * @author Vaadin Ltd + */ +@TestCategory("push") +public class PushVersionInfoTest extends MultiBrowserTest { + + @Test + public void testDisabledPush() { + setDebug(true); + openTestURL(); + + selectInfoTab(); + Assert.assertNull("Found push info server string for disabled Push", + getPushRowValue("Push server version")); + Assert.assertNull("Found push info client string for disabled Push", + getPushRowValue("Push client version")); + } + + @Test + public void testEnabledPush() { + setDebug(true); + openTestURL("enablePush=true"); + + selectInfoTab(); + WebElement pushRow = getPushRowValue("Push server version"); + String atmVersion = findElement(By.className("atmosphere-version")) + .getText(); + Assert.assertTrue("Push row doesn't contain Atmosphere version", + pushRow.getText().contains(atmVersion)); + String jsString = getPushRowValue("Push client version").getText(); + Assert.assertTrue( + "Push client version doesn't contain 'vaadin' string", + jsString.contains("vaadin")); + Assert.assertTrue( + "Push client version doesn't contain 'jquery' string", + jsString.contains("jquery")); + } + + private void selectInfoTab() { + if (isElementPresent(By.className("v-ie8"))) { + + int size = findElements(By.className("v-debugwindow-tab")).size(); + for (int i = 0; i < size; i++) { + WebElement tab = findElement(By + .className("v-debugwindow-tab-selected")); + String title = tab.getAttribute("title"); + if (title != null && title.startsWith("General information")) { + break; + } + Actions actions = new Actions(getDriver()); + actions.sendKeys(Keys.TAB); + actions.sendKeys(Keys.SPACE); + actions.build().perform(); + } + } else { + findElements(By.className("v-debugwindow-tab")).get(0).click(); + findElements(By.className("v-debugwindow-tab")).get(1).click(); + } + } + + private WebElement getPushRowValue(String key) { + List rows = findElements(By.className("v-debugwindow-row")); + for (WebElement row : rows) { + WebElement caption = row.findElement(By.className("caption")); + if (caption.getText().startsWith(key)) { + return row.findElement(By.className("value")); + } + } + return null; + } +} -- cgit v1.2.3 From 3cad153ba405fa60583925db7fee111ec326794f Mon Sep 17 00:00:00 2001 From: Leif Åstrand Date: Tue, 2 Dec 2014 10:24:08 +0200 Subject: Escape themeUri before rewriting CustomLayout HTML. (#15309) Change-Id: I192691ac02bf3a98df1424dead5e4fd6385e395d --- client/src/com/vaadin/client/ui/VCustomLayout.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/com/vaadin/client/ui/VCustomLayout.java b/client/src/com/vaadin/client/ui/VCustomLayout.java index 3b2b007b5b..f5d572007a 100644 --- a/client/src/com/vaadin/client/ui/VCustomLayout.java +++ b/client/src/com/vaadin/client/ui/VCustomLayout.java @@ -158,7 +158,7 @@ public class VCustomLayout extends ComplexPanel { // TODO prefix img src:s here with a regeps, cannot work further with IE - String relImgPrefix = themeUri + "/layouts/"; + String relImgPrefix = Util.escapeAttribute(themeUri + "/layouts/"); // prefix all relative image elements to point to theme dir with a // regexp search -- cgit v1.2.3 From 654846837379db9a76823f5d0e73e5e6bfa8115d Mon Sep 17 00:00:00 2001 From: Denis Anisimov Date: Mon, 29 Sep 2014 20:40:36 +0300 Subject: Allow BeanFieldGroup.setItemDataSource() method accept null (#14731). Change-Id: I7a451f428f0aa3ea7fc322cac6b0e62d830d7987 --- .../com/vaadin/data/fieldgroup/BeanFieldGroup.java | 7 ++--- server/src/com/vaadin/data/util/BeanItem.java | 25 ++++++++++++++++- .../src/com/vaadin/data/util/MethodProperty.java | 10 ++++--- .../component/fieldgroup/BeanFieldGroupTest.java | 31 ++++++++++++++++++++++ 4 files changed, 66 insertions(+), 7 deletions(-) diff --git a/server/src/com/vaadin/data/fieldgroup/BeanFieldGroup.java b/server/src/com/vaadin/data/fieldgroup/BeanFieldGroup.java index e5d53b759d..1f3ee36d58 100644 --- a/server/src/com/vaadin/data/fieldgroup/BeanFieldGroup.java +++ b/server/src/com/vaadin/data/fieldgroup/BeanFieldGroup.java @@ -130,16 +130,17 @@ public class BeanFieldGroup extends FieldGroup { * The bean to use as data source. */ public void setItemDataSource(T bean) { - setItemDataSource(new BeanItem(bean)); + setItemDataSource(new BeanItem(bean, beanType)); } @Override public void setItemDataSource(Item item) { - if (!(item instanceof BeanItem)) { + if (item == null || (item instanceof BeanItem)) { + super.setItemDataSource(item); + } else { throw new RuntimeException(getClass().getSimpleName() + " only supports BeanItems as item data source"); } - super.setItemDataSource(item); } @Override diff --git a/server/src/com/vaadin/data/util/BeanItem.java b/server/src/com/vaadin/data/util/BeanItem.java index 12d9b23d0a..64f30261c2 100644 --- a/server/src/com/vaadin/data/util/BeanItem.java +++ b/server/src/com/vaadin/data/util/BeanItem.java @@ -62,7 +62,30 @@ public class BeanItem extends PropertysetItem { * */ public BeanItem(BT bean) { - this(bean, getPropertyDescriptors((Class) bean.getClass())); + this(bean, (Class) bean.getClass()); + } + + /** + *

+ * Creates a new instance of BeanItem and adds all properties + * of a Java Bean to it. The properties are identified by their respective + * bean names. + *

+ * + *

+ * Note : This version only supports introspectable bean properties and + * their getter and setter methods. Stand-alone is and + * are methods are not supported. + *

+ * + * @param bean + * the Java Bean to copy properties from. + * @param beanClass + * class of the {@code bean} + * + */ + public BeanItem(BT bean, Class beanClass) { + this(bean, getPropertyDescriptors(beanClass)); } /** diff --git a/server/src/com/vaadin/data/util/MethodProperty.java b/server/src/com/vaadin/data/util/MethodProperty.java index 5e6b731571..853f68b711 100644 --- a/server/src/com/vaadin/data/util/MethodProperty.java +++ b/server/src/com/vaadin/data/util/MethodProperty.java @@ -132,7 +132,7 @@ public class MethodProperty extends AbstractProperty { setArguments(getArgs, setArgs, setArgumentIndex); String name = (String) in.readObject(); Class[] paramTypes = SerializerHelper.readClassArray(in); - if (name != null) { + if (instance != null && name != null) { setMethod = instance.getClass().getMethod(name, paramTypes); } else { setMethod = null; @@ -140,7 +140,7 @@ public class MethodProperty extends AbstractProperty { name = (String) in.readObject(); paramTypes = SerializerHelper.readClassArray(in); - if (name != null) { + if (instance != null && name != null) { getMethod = instance.getClass().getMethod(name, paramTypes); } else { getMethod = null; @@ -589,7 +589,11 @@ public class MethodProperty extends AbstractProperty { @Override public T getValue() { try { - return (T) getMethod.invoke(instance, getArgs); + if (instance == null) { + return null; + } else { + return (T) getMethod.invoke(instance, getArgs); + } } catch (final Throwable e) { throw new MethodException(this, e); } diff --git a/server/tests/src/com/vaadin/tests/server/component/fieldgroup/BeanFieldGroupTest.java b/server/tests/src/com/vaadin/tests/server/component/fieldgroup/BeanFieldGroupTest.java index 112d36d884..965fb49479 100644 --- a/server/tests/src/com/vaadin/tests/server/component/fieldgroup/BeanFieldGroupTest.java +++ b/server/tests/src/com/vaadin/tests/server/component/fieldgroup/BeanFieldGroupTest.java @@ -2,12 +2,16 @@ package com.vaadin.tests.server.component.fieldgroup; import static org.junit.Assert.assertEquals; +import java.util.Collection; + import org.junit.Assert; import org.junit.Test; +import com.vaadin.data.Item; import com.vaadin.data.fieldgroup.BeanFieldGroup; import com.vaadin.data.fieldgroup.FieldGroup.CommitException; import com.vaadin.data.fieldgroup.PropertyId; +import com.vaadin.data.util.BeanItem; import com.vaadin.ui.Field; import com.vaadin.ui.TextField; @@ -133,4 +137,31 @@ public class BeanFieldGroupTest { assertEquals(bean.nestedBean.hello, helloField.getValue().toString()); } + @Test + public void setDataSource_nullBean_nullBeanIsSetInDataSource() { + BeanFieldGroup group = new BeanFieldGroup(MyBean.class); + + group.setItemDataSource((MyBean) null); + + BeanItem dataSource = group.getItemDataSource(); + Assert.assertNotNull("Data source is null for null bean", dataSource); + + Collection itemPropertyIds = dataSource.getItemPropertyIds(); + Assert.assertEquals("Unexpected number of properties", 3, + itemPropertyIds.size()); + for (Object id : itemPropertyIds) { + Assert.assertNull("Value for property " + id + " is not null", + dataSource.getItemProperty(id).getValue()); + } + } + + @Test + public void setDataSource_nullItem_nullDataSourceIsSet() { + BeanFieldGroup group = new BeanFieldGroup(MyBean.class); + + group.setItemDataSource((Item) null); + BeanItem dataSource = group.getItemDataSource(); + Assert.assertNull("Group returns not null data source", dataSource); + } + } -- cgit v1.2.3 From 3c59a1f08d6e2288c4bcd0ac74273ae14c4d4e0b Mon Sep 17 00:00:00 2001 From: Leif Åstrand Date: Fri, 14 Nov 2014 15:27:49 +0200 Subject: Escape dynamic and configured theme names in the same way. (#15309) Change-Id: Ib7fd42e6017d0b78e6d5e6bd7f531f0cd6c8c0ab --- server/src/com/vaadin/server/VaadinServlet.java | 6 +-- server/src/com/vaadin/ui/UI.java | 8 +++- .../tests/src/com/vaadin/ui/UIThemeEscaping.java | 43 ++++++++++++++++++++++ 3 files changed, 52 insertions(+), 5 deletions(-) create mode 100644 server/tests/src/com/vaadin/ui/UIThemeEscaping.java diff --git a/server/src/com/vaadin/server/VaadinServlet.java b/server/src/com/vaadin/server/VaadinServlet.java index 4fd1e97a40..d1242676da 100644 --- a/server/src/com/vaadin/server/VaadinServlet.java +++ b/server/src/com/vaadin/server/VaadinServlet.java @@ -573,8 +573,8 @@ public class VaadinServlet extends HttpServlet implements Constants { /** * A helper method to strip away characters that might somehow be used for - * XSS attacs. Leaves at least alphanumeric characters intact. Also removes - * eg. ( and ), so values should be safe in javascript too. + * XSS attacks. Leaves at least alphanumeric characters intact. Also removes + * e.g. '(' and ')', so values should be safe in javascript too. * * @param themeName * @return @@ -583,7 +583,7 @@ public class VaadinServlet extends HttpServlet implements Constants { * version */ @Deprecated - protected static String stripSpecialChars(String themeName) { + public static String stripSpecialChars(String themeName) { StringBuilder sb = new StringBuilder(); char[] charArray = themeName.toCharArray(); for (int i = 0; i < charArray.length; i++) { diff --git a/server/src/com/vaadin/ui/UI.java b/server/src/com/vaadin/ui/UI.java index 78cb5488e8..44948dfb6f 100644 --- a/server/src/com/vaadin/ui/UI.java +++ b/server/src/com/vaadin/ui/UI.java @@ -633,7 +633,11 @@ public abstract class UI extends AbstractSingleComponentContainer implements this.embedId = embedId; // Actual theme - used for finding CustomLayout templates - getState().theme = request.getParameter("theme"); + String unescapedThemeName = request.getParameter("theme"); + if (unescapedThemeName != null) { + // Set theme escapes the name + setTheme(unescapedThemeName); + } getPage().init(request); @@ -1164,7 +1168,7 @@ public abstract class UI extends AbstractSingleComponentContainer implements * The new theme name */ public void setTheme(String theme) { - getState().theme = theme; + getState().theme = VaadinServlet.stripSpecialChars(theme); } /** diff --git a/server/tests/src/com/vaadin/ui/UIThemeEscaping.java b/server/tests/src/com/vaadin/ui/UIThemeEscaping.java new file mode 100644 index 0000000000..ca6782952d --- /dev/null +++ b/server/tests/src/com/vaadin/ui/UIThemeEscaping.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.ui; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.server.VaadinRequest; + +public class UIThemeEscaping { + + @Test + public void testThemeEscaping() { + UI ui = new UI() { + @Override + protected void init(VaadinRequest request) { + // Nothing to do + } + }; + + ui.setTheme("a<å(_\"$"); + + String theme = ui.getTheme(); + + Assert.assertEquals( + "Dangerous characters should be removed from the theme name", + "aå_$", theme); + } + +} -- cgit v1.2.3 From e8868a225504dd90dbecaceb1d6eb9df40116355 Mon Sep 17 00:00:00 2001 From: Sauli Tähkäpää Date: Sun, 23 Nov 2014 14:16:02 +0200 Subject: Fix opacity for disabled links in Valo. (#15253) Change-Id: I865526499a6d55a835758f0194a977c36c10304a --- WebContent/VAADIN/themes/valo/components/_link.scss | 8 ++++++-- uitest/src/com/vaadin/tests/themes/valo/ButtonsAndLinks.java | 5 +++++ uitest/src/com/vaadin/tests/themes/valo/ValoThemeUITest.java | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/WebContent/VAADIN/themes/valo/components/_link.scss b/WebContent/VAADIN/themes/valo/components/_link.scss index b568df1d7b..270de1aace 100644 --- a/WebContent/VAADIN/themes/valo/components/_link.scss +++ b/WebContent/VAADIN/themes/valo/components/_link.scss @@ -20,8 +20,8 @@ $v-link-cursor: pointer !default; /** * * - * @param {string} $primary-stylename (v-link) - - * @param {bool} $include-additional-styles - + * @param {string} $primary-stylename (v-link) - + * @param {bool} $include-additional-styles - * * @group link */ @@ -74,4 +74,8 @@ $v-link-cursor: pointer !default; &:hover { color: lighten($v-link-font-color, 10%); } + + &.v-disabled { + @include opacity($v-disabled-opacity); + } } diff --git a/uitest/src/com/vaadin/tests/themes/valo/ButtonsAndLinks.java b/uitest/src/com/vaadin/tests/themes/valo/ButtonsAndLinks.java index e66cd2668b..9ed48896eb 100644 --- a/uitest/src/com/vaadin/tests/themes/valo/ButtonsAndLinks.java +++ b/uitest/src/com/vaadin/tests/themes/valo/ButtonsAndLinks.java @@ -178,6 +178,11 @@ public class ButtonsAndLinks extends VerticalLayout implements View { link.setIcon(testIcon.get()); link.addStyleName("large"); row.addComponent(link); + + link = new Link("Disabled", new ExternalResource("https://vaadin.com")); + link.setIcon(testIcon.get()); + link.setEnabled(false); + row.addComponent(link); } @Override diff --git a/uitest/src/com/vaadin/tests/themes/valo/ValoThemeUITest.java b/uitest/src/com/vaadin/tests/themes/valo/ValoThemeUITest.java index 92cb837b38..a826d9a8f2 100644 --- a/uitest/src/com/vaadin/tests/themes/valo/ValoThemeUITest.java +++ b/uitest/src/com/vaadin/tests/themes/valo/ValoThemeUITest.java @@ -39,7 +39,7 @@ public class ValoThemeUITest extends MultiBrowserTest { public void buttonsLinks() throws Exception { openTestURL("test"); open("Buttons & Links", "Buttons"); - compareScreen("buttonsLinks"); + compareScreen("buttonsLinks_with_disabled"); } @Test -- cgit v1.2.3 From 758300e27e0110f58794e42bafac6e806ffdfb24 Mon Sep 17 00:00:00 2001 From: Sauli Tähkäpää Date: Thu, 4 Dec 2014 14:36:11 +0200 Subject: Add null check to UI.setTheme. (#15326) Change-Id: I2ab2d24ec05bb618969f59ea15d3a971f88009ca --- server/src/com/vaadin/ui/UI.java | 12 ++-- .../tests/src/com/vaadin/ui/UIThemeEscaping.java | 64 +++++++++++++++++++--- 2 files changed, 61 insertions(+), 15 deletions(-) diff --git a/server/src/com/vaadin/ui/UI.java b/server/src/com/vaadin/ui/UI.java index 44948dfb6f..4bd4b67259 100644 --- a/server/src/com/vaadin/ui/UI.java +++ b/server/src/com/vaadin/ui/UI.java @@ -633,11 +633,7 @@ public abstract class UI extends AbstractSingleComponentContainer implements this.embedId = embedId; // Actual theme - used for finding CustomLayout templates - String unescapedThemeName = request.getParameter("theme"); - if (unescapedThemeName != null) { - // Set theme escapes the name - setTheme(unescapedThemeName); - } + setTheme(request.getParameter("theme")); getPage().init(request); @@ -1168,7 +1164,11 @@ public abstract class UI extends AbstractSingleComponentContainer implements * The new theme name */ public void setTheme(String theme) { - getState().theme = VaadinServlet.stripSpecialChars(theme); + if(theme == null) { + getState().theme = null; + } else { + getState().theme = VaadinServlet.stripSpecialChars(theme); + } } /** diff --git a/server/tests/src/com/vaadin/ui/UIThemeEscaping.java b/server/tests/src/com/vaadin/ui/UIThemeEscaping.java index ca6782952d..236f283823 100644 --- a/server/tests/src/com/vaadin/ui/UIThemeEscaping.java +++ b/server/tests/src/com/vaadin/ui/UIThemeEscaping.java @@ -15,29 +15,75 @@ */ package com.vaadin.ui; -import org.junit.Assert; +import com.vaadin.server.VaadinRequest; +import org.junit.Before; import org.junit.Test; -import com.vaadin.server.VaadinRequest; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class UIThemeEscaping { - @Test - public void testThemeEscaping() { - UI ui = new UI() { + private UI ui; + + private void initUiWithTheme(String theme) { + VaadinRequest request = getRequestWithTheme(theme); + + ui.doInit(request, 1234, "foobar"); + } + + private VaadinRequest getRequestWithTheme(String theme) { + VaadinRequest request = mock(VaadinRequest.class); + + when(request.getParameter("theme")).thenReturn(theme); + + return request; + } + + @Before + public void setup() { + ui = new UI() { @Override protected void init(VaadinRequest request) { // Nothing to do } }; + } + @Test + public void dangerousCharactersAreRemoved() { ui.setTheme("a<å(_\"$"); - String theme = ui.getTheme(); + assertThat(ui.getTheme(), is("aå_$")); + } + + @Test + public void nullThemeIsSet() { + ui.setTheme("foobar"); + + ui.setTheme(null); - Assert.assertEquals( - "Dangerous characters should be removed from the theme name", - "aå_$", theme); + assertThat(ui.getTheme(), is(nullValue())); } + @Test + public void themeIsSetOnInit() { + ui.setTheme("foobar"); + + initUiWithTheme("bar"); + + assertThat(ui.getTheme(), is("bar")); + } + + @Test + public void nullThemeIsSetOnInit() { + ui.setTheme("foobar"); + + initUiWithTheme(null); + + assertThat(ui.getTheme(), is(nullValue())); + } } -- cgit v1.2.3 From a2013c9f9be5fb50406f9b8e3133934648ee64fa Mon Sep 17 00:00:00 2001 From: Leif Åstrand Date: Thu, 27 Nov 2014 18:19:11 +0200 Subject: Add LICENSE file according to GitHub conventions Change-Id: I5e402d399729800435589cf8c186c96ecbdfc698 --- LICENSE | 201 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..5c304d1a4a --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. -- cgit v1.2.3 From 66c3dd2bf87ac3b021365b7a7d89180fb4304d8c Mon Sep 17 00:00:00 2001 From: Ilya Ermakov Date: Fri, 28 Nov 2014 13:09:38 +0300 Subject: SQLContainer removeItem Error when isModified (#8802) This patch makes commit() work properly if an item was modified and later deleted. Change-Id: I5a00024112e7b6bb7ab3750c292a872937f03af9 --- .../data/util/sqlcontainer/SQLContainer.java | 26 ++++++++++++---------- .../sqlcontainer/SQLContainerTableQueryTest.java | 14 ++++++++++++ 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/server/src/com/vaadin/data/util/sqlcontainer/SQLContainer.java b/server/src/com/vaadin/data/util/sqlcontainer/SQLContainer.java index 683140d279..70b392ab80 100644 --- a/server/src/com/vaadin/data/util/sqlcontainer/SQLContainer.java +++ b/server/src/com/vaadin/data/util/sqlcontainer/SQLContainer.java @@ -1020,18 +1020,20 @@ public class SQLContainer implements Container, Container.Filterable, } /* Perform buffered modifications */ for (RowItem item : modifiedItems) { - if (queryDelegate.storeRow(item) > 0) { - /* - * Also reset the modified state in the item in case it is - * reused e.g. in a form. - */ - item.commit(); - } else { - queryDelegate.rollback(); - refresh(); - throw new ConcurrentModificationException( - "Item with the ID '" + item.getId() - + "' has been externally modified."); + if (!removedItems.containsKey(item.getId())) { + if (queryDelegate.storeRow(item) > 0) { + /* + * Also reset the modified state in the item in case it + * is reused e.g. in a form. + */ + item.commit(); + } else { + queryDelegate.rollback(); + refresh(); + throw new ConcurrentModificationException( + "Item with the ID '" + item.getId() + + "' has been externally modified."); + } } } /* Perform buffered additions */ diff --git a/server/tests/src/com/vaadin/data/util/sqlcontainer/SQLContainerTableQueryTest.java b/server/tests/src/com/vaadin/data/util/sqlcontainer/SQLContainerTableQueryTest.java index d907f12321..93a27352a5 100644 --- a/server/tests/src/com/vaadin/data/util/sqlcontainer/SQLContainerTableQueryTest.java +++ b/server/tests/src/com/vaadin/data/util/sqlcontainer/SQLContainerTableQueryTest.java @@ -1136,6 +1136,20 @@ public class SQLContainerTableQueryTest { .getValue()); } + @Test + public void commit_removeModifiedItem_shouldSucceed() throws SQLException { + TableQuery query = new TableQuery("people", connectionPool, + SQLTestsConstants.sqlGen); + SQLContainer container = new SQLContainer(query); + int size = container.size(); + Object key = container.firstItemId(); + Item row = container.getItem(key); + row.getItemProperty("NAME").setValue("Pekka"); + Assert.assertTrue(container.removeItem(key)); + container.commit(); + Assert.assertEquals(size - 1, container.size()); + } + @Test public void rollback_tableItemAdded_discardsAddedItem() throws SQLException { SQLContainer container = new SQLContainer(new TableQuery("people", -- cgit v1.2.3 From bf4e325ac6387cac0afc8096b3ca65b88bf9fdfd Mon Sep 17 00:00:00 2001 From: Fabian Lange Date: Mon, 24 Nov 2014 12:11:08 +0100 Subject: UIInitHandler emits Content-Length when writing json response (#15271) This prevents chunked-transfer mode and allows server infrastructure to decide correctly on things like compression which benefits from knowing a content length. Change-Id: I4e969e0874b506d0f61526662ee78418987937c4 --- .../vaadin/server/communication/UIInitHandler.java | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/server/src/com/vaadin/server/communication/UIInitHandler.java b/server/src/com/vaadin/server/communication/UIInitHandler.java index 8e61370d85..1216d2b689 100644 --- a/server/src/com/vaadin/server/communication/UIInitHandler.java +++ b/server/src/com/vaadin/server/communication/UIInitHandler.java @@ -17,7 +17,7 @@ package com.vaadin.server.communication; import java.io.IOException; -import java.io.OutputStreamWriter; +import java.io.OutputStream; import java.io.StringWriter; import java.util.List; import java.util.logging.Level; @@ -110,18 +110,13 @@ public abstract class UIInitHandler extends SynchronizedRequestHandler { // iOS 6 Safari requires this (#9732) response.setHeader("Cache-Control", "no-cache"); - // NOTE! GateIn requires, for some weird reason, getOutputStream - // to be used instead of getWriter() (it seems to interpret - // application/json as a binary content type) - OutputStreamWriter outputWriter = new OutputStreamWriter( - response.getOutputStream(), "UTF-8"); - try { - outputWriter.write(json); - // NOTE GateIn requires the buffers to be flushed to work - outputWriter.flush(); - } finally { - outputWriter.close(); - } + byte[] b = json.getBytes("UTF-8"); + response.setHeader("Content-Length", String.valueOf(b.length)); + + OutputStream outputStream = response.getOutputStream(); + outputStream.write(b); + // NOTE GateIn requires the buffers to be flushed to work + outputStream.flush(); return true; } -- cgit v1.2.3 From a28d6c2098e738dfd78e1df929ce689a2ceb6c2a Mon Sep 17 00:00:00 2001 From: Anna Miroshnik Date: Thu, 4 Dec 2014 15:52:27 +0300 Subject: Add new StringTo Converters (#14583) DefaultConverterFactory was updated, added missed types. Change-Id: I65cffc7f91df6f40e132b45e325e15f029f9848c --- .../converter/AbstractStringToNumberConverter.java | 1 + .../util/converter/DefaultConverterFactory.java | 7 ++++++ .../converter/StringToBigIntegerConverter.java | 11 ++++++--- .../data/util/converter/StringToByteConverter.java | 2 +- .../util/converter/StringToShortConverter.java | 2 +- .../converter/TestStringToBigIntegerConverter.java | 28 ++++++++++++---------- .../data/converter/TestStringToByteConverter.java | 16 ++++++++----- .../data/converter/TestStringToShortConverter.java | 15 ++++++++---- 8 files changed, 54 insertions(+), 28 deletions(-) diff --git a/server/src/com/vaadin/data/util/converter/AbstractStringToNumberConverter.java b/server/src/com/vaadin/data/util/converter/AbstractStringToNumberConverter.java index 9054f258c7..81f1a85f89 100644 --- a/server/src/com/vaadin/data/util/converter/AbstractStringToNumberConverter.java +++ b/server/src/com/vaadin/data/util/converter/AbstractStringToNumberConverter.java @@ -88,6 +88,7 @@ public abstract class AbstractStringToNumberConverter implements // Convert "" to null return null; } + return parsedValue; } diff --git a/server/src/com/vaadin/data/util/converter/DefaultConverterFactory.java b/server/src/com/vaadin/data/util/converter/DefaultConverterFactory.java index 26613c5d02..3a1f1a4252 100644 --- a/server/src/com/vaadin/data/util/converter/DefaultConverterFactory.java +++ b/server/src/com/vaadin/data/util/converter/DefaultConverterFactory.java @@ -17,6 +17,7 @@ package com.vaadin.data.util.converter; import java.math.BigDecimal; +import java.math.BigInteger; import java.util.Date; import java.util.logging.Logger; @@ -112,6 +113,12 @@ public class DefaultConverterFactory implements ConverterFactory { return new StringToDateConverter(); } else if (Enum.class.isAssignableFrom(sourceType)) { return new StringToEnumConverter(); + } else if (BigInteger.class.isAssignableFrom(sourceType)) { + return new StringToBigIntegerConverter(); + } else if (Short.class.isAssignableFrom(sourceType)) { + return new StringToShortConverter(); + } else if (Byte.class.isAssignableFrom(sourceType)) { + return new StringToByteConverter(); } else { return null; } diff --git a/server/src/com/vaadin/data/util/converter/StringToBigIntegerConverter.java b/server/src/com/vaadin/data/util/converter/StringToBigIntegerConverter.java index 1e830c1fd2..d176ac2e0d 100644 --- a/server/src/com/vaadin/data/util/converter/StringToBigIntegerConverter.java +++ b/server/src/com/vaadin/data/util/converter/StringToBigIntegerConverter.java @@ -15,6 +15,7 @@ */ package com.vaadin.data.util.converter; +import java.math.BigDecimal; import java.math.BigInteger; import java.text.DecimalFormat; import java.text.NumberFormat; @@ -32,7 +33,7 @@ import java.util.Locale; *

* * @author Vaadin Ltd - * @since 7.3.1 + * @since */ public class StringToBigIntegerConverter extends AbstractStringToNumberConverter { @@ -41,7 +42,7 @@ public class StringToBigIntegerConverter extends protected NumberFormat getFormat(Locale locale) { NumberFormat numberFormat = super.getFormat(locale); if (numberFormat instanceof DecimalFormat) { - ((DecimalFormat) numberFormat).setParseIntegerOnly(true); + ((DecimalFormat) numberFormat).setParseBigDecimal(true); } return numberFormat; @@ -51,7 +52,11 @@ public class StringToBigIntegerConverter extends public BigInteger convertToModel(String value, Class targetType, Locale locale) throws com.vaadin.data.util.converter.Converter.ConversionException { - return (BigInteger) convertToNumber(value, BigInteger.class, locale); + + BigDecimal bigDecimalValue = (BigDecimal) convertToNumber(value, + BigDecimal.class, locale); + + return (bigDecimalValue != null) ? bigDecimalValue.toBigInteger() : null; } @Override diff --git a/server/src/com/vaadin/data/util/converter/StringToByteConverter.java b/server/src/com/vaadin/data/util/converter/StringToByteConverter.java index b14182dc3f..26f52d108a 100644 --- a/server/src/com/vaadin/data/util/converter/StringToByteConverter.java +++ b/server/src/com/vaadin/data/util/converter/StringToByteConverter.java @@ -28,7 +28,7 @@ import java.util.Locale; *

* * @author Vaadin Ltd - * @since 7.3.1 + * @since */ public class StringToByteConverter extends AbstractStringToNumberConverter { diff --git a/server/src/com/vaadin/data/util/converter/StringToShortConverter.java b/server/src/com/vaadin/data/util/converter/StringToShortConverter.java index 3761d11227..4ee085286f 100644 --- a/server/src/com/vaadin/data/util/converter/StringToShortConverter.java +++ b/server/src/com/vaadin/data/util/converter/StringToShortConverter.java @@ -28,7 +28,7 @@ import java.util.Locale; *

* * @author Vaadin Ltd - * @since 7.3.1 + * @since */ public class StringToShortConverter extends AbstractStringToNumberConverter { diff --git a/server/tests/src/com/vaadin/tests/data/converter/TestStringToBigIntegerConverter.java b/server/tests/src/com/vaadin/tests/data/converter/TestStringToBigIntegerConverter.java index 641f18c865..8d493609fe 100644 --- a/server/tests/src/com/vaadin/tests/data/converter/TestStringToBigIntegerConverter.java +++ b/server/tests/src/com/vaadin/tests/data/converter/TestStringToBigIntegerConverter.java @@ -15,39 +15,43 @@ */ package com.vaadin.tests.data.converter; -import java.math.BigDecimal; +import java.math.BigInteger; import java.util.Locale; import junit.framework.TestCase; -import com.vaadin.data.util.converter.StringToBigDecimalConverter; +import com.vaadin.data.util.converter.StringToBigIntegerConverter; public class TestStringToBigIntegerConverter extends TestCase { - StringToBigDecimalConverter converter = new StringToBigDecimalConverter(); + StringToBigIntegerConverter converter = new StringToBigIntegerConverter(); public void testNullConversion() { - assertEquals(null, - converter.convertToModel(null, BigDecimal.class, null)); + assertEquals("Null value was converted incorrectly", null, + converter.convertToModel(null, BigInteger.class, null)); } public void testEmptyStringConversion() { - assertEquals(null, converter.convertToModel("", BigDecimal.class, null)); + assertEquals("Empty value was converted incorrectly", null, + converter.convertToModel("", BigInteger.class, null)); } public void testValueParsing() { - BigDecimal converted = converter.convertToModel("10", BigDecimal.class, - null); - BigDecimal expected = new BigDecimal(10); - assertEquals(expected, converted); + String bigInt = "1180591620717411303424"; // 2^70 > 2^63 - 1 + BigInteger converted = converter.convertToModel(bigInt, + BigInteger.class, null); + BigInteger expected = new BigInteger(bigInt); + assertEquals("Value bigger than max long was converted incorrectly", + expected, converted); } public void testValueFormatting() { - BigDecimal bd = new BigDecimal(1000); + BigInteger bd = new BigInteger("1000"); String expected = "1.000"; String converted = converter.convertToPresentation(bd, String.class, Locale.GERMAN); - assertEquals(expected, converted); + assertEquals("Value with specific locale was converted incorrectly", + expected, converted); } } diff --git a/server/tests/src/com/vaadin/tests/data/converter/TestStringToByteConverter.java b/server/tests/src/com/vaadin/tests/data/converter/TestStringToByteConverter.java index 440d056c06..19a68fbfdb 100644 --- a/server/tests/src/com/vaadin/tests/data/converter/TestStringToByteConverter.java +++ b/server/tests/src/com/vaadin/tests/data/converter/TestStringToByteConverter.java @@ -16,25 +16,28 @@ public class TestStringToByteConverter extends TestCase { converter); public void testNullConversion() { - assertEquals(null, converter.convertToModel(null, Byte.class, null)); + assertEquals("Null value was converted incorrectly", null, + converter.convertToModel(null, Byte.class, null)); } public void testReverseNullConversion() { - assertEquals(null, + assertEquals("Null value reversely was converted incorrectly", null, reverseConverter.convertToModel(null, String.class, null)); } public void testEmptyStringConversion() { - assertEquals(null, converter.convertToModel("", Byte.class, null)); + assertEquals("Empty value was converted incorrectly", null, + converter.convertToModel("", Byte.class, null)); } public void testValueConversion() { - assertEquals(Byte.valueOf((byte) 10), + assertEquals("Byte value was converted incorrectly", + Byte.valueOf((byte) 10), converter.convertToModel("10", Byte.class, null)); } public void testReverseValueConversion() { - assertEquals( + assertEquals("Byte value reversely was converted incorrectly", reverseConverter.convertToModel((byte) 10, String.class, null), "10"); } @@ -43,7 +46,8 @@ public class TestStringToByteConverter extends TestCase { byte b = converter.convertToModel("127", Byte.class, null); Assert.assertEquals(Byte.MAX_VALUE, b); b = converter.convertToModel("-128", Byte.class, null); - assertEquals(Byte.MIN_VALUE, b); + assertEquals("Min byte value was converted incorrectly", + Byte.MIN_VALUE, b); } public void testValueOutOfRange() { diff --git a/server/tests/src/com/vaadin/tests/data/converter/TestStringToShortConverter.java b/server/tests/src/com/vaadin/tests/data/converter/TestStringToShortConverter.java index 35547d2570..542c580025 100644 --- a/server/tests/src/com/vaadin/tests/data/converter/TestStringToShortConverter.java +++ b/server/tests/src/com/vaadin/tests/data/converter/TestStringToShortConverter.java @@ -16,25 +16,29 @@ public class TestStringToShortConverter extends TestCase { converter); public void testNullConversion() { - assertEquals(null, converter.convertToModel(null, Short.class, null)); + assertEquals("Null value was converted incorrectly", null, + converter.convertToModel(null, Short.class, null)); } public void testReverseNullConversion() { - assertEquals(null, + assertEquals("Null value reversely was converted incorrectly", null, reverseConverter.convertToModel(null, String.class, null)); } public void testEmptyStringConversion() { - assertEquals(null, converter.convertToModel("", Short.class, null)); + assertEquals("Empty value was converted incorrectly", null, + converter.convertToModel("", Short.class, null)); } public void testValueConversion() { - assertEquals(Short.valueOf((short) 10), + assertEquals("Short value was converted incorrectly", + Short.valueOf((short) 10), converter.convertToModel("10", Short.class, null)); } public void testReverseValueConversion() { assertEquals( + "Short value reversely was converted incorrectly", reverseConverter.convertToModel((short) 10, String.class, null), "10"); } @@ -43,7 +47,8 @@ public class TestStringToShortConverter extends TestCase { short b = converter.convertToModel("32767", Short.class, null); Assert.assertEquals(Short.MAX_VALUE, b); b = converter.convertToModel("-32768", Short.class, null); - assertEquals(Short.MIN_VALUE, b); + assertEquals("Min short value was converted incorrectly", + Short.MIN_VALUE, b); } public void testValueOutOfRange() { -- cgit v1.2.3 From 449e5e496898b56e07f65e0b6be830024492c5ce Mon Sep 17 00:00:00 2001 From: Artur Signell Date: Mon, 1 Dec 2014 22:18:07 +0200 Subject: Basic tests for default converter factory Change-Id: I2d9df8a62635164f04d9109e37746686589b696a --- .../converter/TestDefaultConverterFactory.java | 128 +++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 server/tests/src/com/vaadin/tests/data/converter/TestDefaultConverterFactory.java diff --git a/server/tests/src/com/vaadin/tests/data/converter/TestDefaultConverterFactory.java b/server/tests/src/com/vaadin/tests/data/converter/TestDefaultConverterFactory.java new file mode 100644 index 0000000000..e1becf43e1 --- /dev/null +++ b/server/tests/src/com/vaadin/tests/data/converter/TestDefaultConverterFactory.java @@ -0,0 +1,128 @@ +/* + * 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.data.converter; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Date; +import java.util.Locale; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.data.util.converter.DefaultConverterFactory; + +public class TestDefaultConverterFactory { + + private DefaultConverterFactory factory = new DefaultConverterFactory(); + + @Test + public void stringToBigDecimal() { + assertConverter("14", new BigDecimal("14")); + } + + @Test + public void stringToBigInteger() { + assertConverter("14", new BigInteger("14")); + } + + @Test + public void stringToDouble() { + assertConverter("14", new Double("14")); + } + + @Test + public void stringToFloat() { + assertConverter("14", new Float("14")); + } + + @Test + public void stringToInteger() { + assertConverter("14", new Integer("14")); + } + + @Test + public void stringToLong() { + assertConverter("14", new Long("14")); + } + + @SuppressWarnings("deprecation") + @Test + public void stringToDate() { + assertConverter("Oct 12, 2014 12:00:00 AM", new Date(2014 - 1900, + 10 - 1, 12)); + } + + @Test + public void sqlDateToDate() { + long l = 1413071210000L; + assertConverter(new java.sql.Date(l), new java.util.Date(l)); + } + + @SuppressWarnings("deprecation") + @Test + public void longToDate() { + assertConverter(1413061200000L, new Date(2014 - 1900, 10 - 1, 12)); + } + + public enum Foo { + BAR, BAZ; + } + + @Test + public void stringToEnum() { + assertConverter("Bar", Foo.BAR); + } + + @Test + public void stringToShort() { + assertConverter("14", new Short("14")); + } + + @Test + public void stringToByte() { + assertConverter("14", new Byte("14")); + } + + private void assertConverter(T t, U u) { + Class tClass = (Class) t.getClass(); + Class uClass = (Class) u.getClass(); + + U tConvertedToU = factory.createConverter(tClass, uClass) + .convertToModel(t, uClass, Locale.ENGLISH); + Assert.assertEquals( + "Incorrect type of value converted from " + + tClass.getSimpleName() + " to " + + uClass.getSimpleName(), uClass, + tConvertedToU.getClass()); + Assert.assertEquals( + "Incorrect conversion of " + t + " to " + + uClass.getSimpleName(), u, tConvertedToU); + + T uConvertedToT = factory.createConverter(uClass, tClass) + .convertToModel(u, tClass, Locale.ENGLISH); + Assert.assertEquals( + "Incorrect type of value converted from " + + uClass.getSimpleName() + " to " + + tClass.getSimpleName(), tClass, + uConvertedToT.getClass()); + Assert.assertEquals( + "Incorrect conversion of " + u + " to " + + tClass.getSimpleName(), t, uConvertedToT); + + } + +} -- cgit v1.2.3 From 285b4bc21f85a4570b5f3b45fc4fd120104b4d11 Mon Sep 17 00:00:00 2001 From: Jouni Koivuviita Date: Thu, 4 Dec 2014 15:58:07 +0200 Subject: Fix opacity for disabled checkboxes and option groups. (#15239) Change-Id: I2d09a116d07621053f2fc9524f95e47bf7fc834e --- .../VAADIN/themes/valo/components/_checkbox.scss | 36 ++++++++++------------ .../com/vaadin/tests/themes/valo/CheckBoxes.java | 16 ++++++++++ .../vaadin/tests/themes/valo/ValoThemeUITest.java | 2 +- 3 files changed, 34 insertions(+), 20 deletions(-) diff --git a/WebContent/VAADIN/themes/valo/components/_checkbox.scss b/WebContent/VAADIN/themes/valo/components/_checkbox.scss index 3c418ec9b7..7283c4cbbf 100644 --- a/WebContent/VAADIN/themes/valo/components/_checkbox.scss +++ b/WebContent/VAADIN/themes/valo/components/_checkbox.scss @@ -99,7 +99,7 @@ } & ~ label:before { - @include valo-button-style($background-color: $background-color, $unit-size: $size, $border-radius: min(round($size/3), $v-border-radius)); + @include valo-button-style($background-color: $background-color, $unit-size: $size, $border-radius: min(round($size/3), $v-border-radius), $states: normal); padding: 0; height: round($size); } @@ -119,24 +119,6 @@ &:checked ~ label:after { color: $selection-color; } - - &[disabled] { - ~ label, - ~ label .v-icon, - ~ .v-icon { - cursor: default; - } - - ~ label:before, - ~ label:after { - @include opacity($v-disabled-opacity); - } - - &:active ~ label:after { - background: transparent; - } - } - } & > .v-icon, @@ -146,4 +128,20 @@ cursor: pointer; } + &.v-disabled { + > label, + > .v-icon { + cursor: default; + @include opacity($v-disabled-opacity); + } + + > label > .v-icon { + cursor: default; + } + + :root & > input:active ~ label:after { + background: transparent; + } + } + } diff --git a/uitest/src/com/vaadin/tests/themes/valo/CheckBoxes.java b/uitest/src/com/vaadin/tests/themes/valo/CheckBoxes.java index c7a2610a21..c79447bd86 100644 --- a/uitest/src/com/vaadin/tests/themes/valo/CheckBoxes.java +++ b/uitest/src/com/vaadin/tests/themes/valo/CheckBoxes.java @@ -79,6 +79,11 @@ public class CheckBoxes extends VerticalLayout implements View { check.addStyleName("large"); row.addComponent(check); + check = new CheckBox("Disabled", true); + check.setEnabled(false); + check.setIcon(testIcon.get()); + row.addComponent(check); + h1 = new Label("Option Groups"); h1.addStyleName("h1"); addComponent(h1); @@ -184,6 +189,17 @@ public class CheckBoxes extends VerticalLayout implements View { options.setItemIcon(two, testIcon.get()); options.setItemIcon("Option Three", testIcon.get()); row.addComponent(options); + + options = new OptionGroup("Disabled items"); + options.setEnabled(false); + options.addItem("Option One"); + options.addItem("Option Two"); + options.addItem("Option Three"); + options.select("Option One"); + options.setItemIcon("Option One", testIcon.get()); + options.setItemIcon("Option Two", testIcon.get()); + options.setItemIcon("Option Three", testIcon.get(true)); + row.addComponent(options); } @Override diff --git a/uitest/src/com/vaadin/tests/themes/valo/ValoThemeUITest.java b/uitest/src/com/vaadin/tests/themes/valo/ValoThemeUITest.java index a826d9a8f2..13b0c7144c 100644 --- a/uitest/src/com/vaadin/tests/themes/valo/ValoThemeUITest.java +++ b/uitest/src/com/vaadin/tests/themes/valo/ValoThemeUITest.java @@ -85,7 +85,7 @@ public class ValoThemeUITest extends MultiBrowserTest { public void checkboxes() throws Exception { openTestURL("test"); open("Check Boxes & Option Groups", "Check Boxes"); - compareScreen("checkboxes"); + compareScreen("checkboxes_with_disabled"); } @Test -- cgit v1.2.3 From 7e8b23a73a5dd1bef54b8fc5ddc4d3c431c298af Mon Sep 17 00:00:00 2001 From: Anna Miroshnik Date: Mon, 8 Dec 2014 17:25:00 +0300 Subject: Hierarchy change markAsDirty to AbstractComponentContainer (#14227) Change-Id: I7a05ad72dfa3d16d85aa4d0cdd235572ec01e31a --- server/src/com/vaadin/ui/AbsoluteLayout.java | 2 - .../com/vaadin/ui/AbstractComponentContainer.java | 2 + server/src/com/vaadin/ui/AbstractSplitPanel.java | 2 - server/src/com/vaadin/ui/CssLayout.java | 4 -- server/src/com/vaadin/ui/CustomLayout.java | 4 +- server/src/com/vaadin/ui/GridLayout.java | 3 -- server/src/com/vaadin/ui/TabSheet.java | 11 +++-- .../ComponentAttachDetachListenerTest.java | 50 ++++++++++++++++++++++ .../tests/minitutorials/v7a2/WidgetContainer.java | 2 - 9 files changed, 59 insertions(+), 21 deletions(-) diff --git a/server/src/com/vaadin/ui/AbsoluteLayout.java b/server/src/com/vaadin/ui/AbsoluteLayout.java index afc73f5ecc..af47981db6 100644 --- a/server/src/com/vaadin/ui/AbsoluteLayout.java +++ b/server/src/com/vaadin/ui/AbsoluteLayout.java @@ -153,7 +153,6 @@ public class AbsoluteLayout extends AbstractLayout implements internalRemoveComponent(c); throw e; } - markAsDirty(); } /** @@ -197,7 +196,6 @@ public class AbsoluteLayout extends AbstractLayout implements public void removeComponent(Component c) { internalRemoveComponent(c); super.removeComponent(c); - markAsDirty(); } /** diff --git a/server/src/com/vaadin/ui/AbstractComponentContainer.java b/server/src/com/vaadin/ui/AbstractComponentContainer.java index e70b0fa0ce..1095331602 100644 --- a/server/src/com/vaadin/ui/AbstractComponentContainer.java +++ b/server/src/com/vaadin/ui/AbstractComponentContainer.java @@ -209,6 +209,7 @@ public abstract class AbstractComponentContainer extends AbstractComponent c.setParent(this); fireComponentAttachEvent(c); + markAsDirty(); } /** @@ -223,6 +224,7 @@ public abstract class AbstractComponentContainer extends AbstractComponent if (equals(c.getParent())) { c.setParent(null); fireComponentDetachEvent(c); + markAsDirty(); } } diff --git a/server/src/com/vaadin/ui/AbstractSplitPanel.java b/server/src/com/vaadin/ui/AbstractSplitPanel.java index e9b37f8cff..a78f192fa2 100644 --- a/server/src/com/vaadin/ui/AbstractSplitPanel.java +++ b/server/src/com/vaadin/ui/AbstractSplitPanel.java @@ -214,7 +214,6 @@ public abstract class AbstractSplitPanel extends AbstractComponentContainer { } else if (c == getSecondComponent()) { getState().secondChild = null; } - markAsDirty(); } /* @@ -256,7 +255,6 @@ public abstract class AbstractSplitPanel extends AbstractComponentContainer { } else if (oldComponent == getSecondComponent()) { setSecondComponent(newComponent); } - markAsDirty(); } /** diff --git a/server/src/com/vaadin/ui/CssLayout.java b/server/src/com/vaadin/ui/CssLayout.java index e7b63cc87a..350423576f 100644 --- a/server/src/com/vaadin/ui/CssLayout.java +++ b/server/src/com/vaadin/ui/CssLayout.java @@ -118,7 +118,6 @@ public class CssLayout extends AbstractLayout implements LayoutClickNotifier { components.add(c); try { super.addComponent(c); - markAsDirty(); } catch (IllegalArgumentException e) { components.remove(c); throw e; @@ -141,7 +140,6 @@ public class CssLayout extends AbstractLayout implements LayoutClickNotifier { components.addFirst(c); try { super.addComponent(c); - markAsDirty(); } catch (IllegalArgumentException e) { components.remove(c); throw e; @@ -170,7 +168,6 @@ public class CssLayout extends AbstractLayout implements LayoutClickNotifier { components.add(index, c); try { super.addComponent(c); - markAsDirty(); } catch (IllegalArgumentException e) { components.remove(c); throw e; @@ -187,7 +184,6 @@ public class CssLayout extends AbstractLayout implements LayoutClickNotifier { public void removeComponent(Component c) { components.remove(c); super.removeComponent(c); - markAsDirty(); } /** diff --git a/server/src/com/vaadin/ui/CustomLayout.java b/server/src/com/vaadin/ui/CustomLayout.java index f4fe7fa66c..a9c266b0b9 100644 --- a/server/src/com/vaadin/ui/CustomLayout.java +++ b/server/src/com/vaadin/ui/CustomLayout.java @@ -144,8 +144,8 @@ public class CustomLayout extends AbstractLayout implements LegacyComponent { } slots.put(location, c); getState().childLocations.put(c, location); - c.setParent(this); - fireComponentAttachEvent(c); + + super.addComponent(c); } /** diff --git a/server/src/com/vaadin/ui/GridLayout.java b/server/src/com/vaadin/ui/GridLayout.java index 0dd16a03e7..96854c5b1b 100644 --- a/server/src/com/vaadin/ui/GridLayout.java +++ b/server/src/com/vaadin/ui/GridLayout.java @@ -255,8 +255,6 @@ public class GridLayout extends AbstractLayout implements cursorY = row1; } } - - markAsDirty(); } /** @@ -390,7 +388,6 @@ public class GridLayout extends AbstractLayout implements getState().childData.remove(component); components.remove(component); - super.removeComponent(component); } diff --git a/server/src/com/vaadin/ui/TabSheet.java b/server/src/com/vaadin/ui/TabSheet.java index d3410464a2..88002104b1 100644 --- a/server/src/com/vaadin/ui/TabSheet.java +++ b/server/src/com/vaadin/ui/TabSheet.java @@ -195,7 +195,6 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, if (component != null && components.contains(component)) { int componentIndex = components.indexOf(component); - super.removeComponent(component); keyMapper.remove(component); components.remove(component); @@ -232,7 +231,6 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, fireSelectedTabChange(); } } - markAsDirty(); } } @@ -394,8 +392,9 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, setSelected(tabComponent); fireSelectedTabChange(); } + super.addComponent(tabComponent); - markAsDirty(); + return tab; } } @@ -967,16 +966,16 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, /** * Gets the icon alt text for the tab. - * + * * @since 7.2 */ public String getIconAlternateText(); /** * Sets the icon alt text for the tab. - * + * * @since 7.2 - * + * * @param iconAltText * the icon to set */ diff --git a/server/tests/src/com/vaadin/tests/server/components/ComponentAttachDetachListenerTest.java b/server/tests/src/com/vaadin/tests/server/components/ComponentAttachDetachListenerTest.java index df515795eb..d8b366ffbc 100644 --- a/server/tests/src/com/vaadin/tests/server/components/ComponentAttachDetachListenerTest.java +++ b/server/tests/src/com/vaadin/tests/server/components/ComponentAttachDetachListenerTest.java @@ -9,6 +9,7 @@ import com.vaadin.ui.AbsoluteLayout.ComponentPosition; import com.vaadin.ui.AbstractOrderedLayout; import com.vaadin.ui.Component; import com.vaadin.ui.CssLayout; +import com.vaadin.ui.CustomLayout; import com.vaadin.ui.GridLayout; import com.vaadin.ui.GridLayout.Area; import com.vaadin.ui.HasComponents; @@ -25,6 +26,7 @@ public class ComponentAttachDetachListenerTest extends TestCase { private GridLayout gridlayout; private AbsoluteLayout absolutelayout; private CssLayout csslayout; + private CustomLayout customlayout; // General variables private int attachCounter = 0; @@ -143,6 +145,10 @@ public class ComponentAttachDetachListenerTest extends TestCase { csslayout = new CssLayout(); csslayout.addComponentAttachListener(new MyAttachListener()); csslayout.addComponentDetachListener(new MyDetachListener()); + + customlayout = new CustomLayout("
"); + customlayout.addComponentAttachListener(new MyAttachListener()); + customlayout.addComponentDetachListener(new MyDetachListener()); } public void testOrderedLayoutAttachListener() { @@ -342,4 +348,48 @@ public class ComponentAttachDetachListenerTest extends TestCase { // The detached component should not be found in the container assertFalse(foundInContainer); } + + public void testCustomLayoutAttachListener() { + // Reset state variables + resetVariables(); + + // Add component -> Should trigger attach listener + Component comp = new Label(); + customlayout.addComponent(comp, "loc"); + + assertEquals("Attach counter should get incremented", 1, attachCounter); + + assertSame("The attached component should be the label", comp, + attachedComponent); + + assertSame("The attached target should be the layout", customlayout, + attachTarget); + + assertTrue("The attached component should be found in the container", + foundInContainer); + } + + public void testCustomLayoutDetachListener() { + // Add a component to detach + Component comp = new Label(); + customlayout.addComponent(comp); + + // Reset state variables (since they are set by the attach listener) + resetVariables(); + + // Detach the component -> triggers the detach listener + customlayout.removeComponent(comp); + + assertEquals("Detach counter should get incremented", 1, detachCounter); + + assertSame("The detached component should be the label", comp, + detachedComponent); + + assertSame("The detached target should be the layout", customlayout, + detachedTarget); + + assertFalse( + "The detached component should not be found in the container", + foundInContainer); + } } diff --git a/uitest/src/com/vaadin/tests/minitutorials/v7a2/WidgetContainer.java b/uitest/src/com/vaadin/tests/minitutorials/v7a2/WidgetContainer.java index 8c14ba8bd7..850fa1044f 100644 --- a/uitest/src/com/vaadin/tests/minitutorials/v7a2/WidgetContainer.java +++ b/uitest/src/com/vaadin/tests/minitutorials/v7a2/WidgetContainer.java @@ -15,14 +15,12 @@ public class WidgetContainer extends AbstractComponentContainer { public void addComponent(Component c) { children.add(c); super.addComponent(c); - markAsDirty(); } @Override public void removeComponent(Component c) { children.remove(c); super.removeComponent(c); - markAsDirty(); } @Override -- cgit v1.2.3 From 206055708b0a8e1c17a8c63d482a5e202d3ebf6d Mon Sep 17 00:00:00 2001 From: Teemu Pöntelin Date: Mon, 8 Dec 2014 23:21:08 +0200 Subject: Fix issues when using java.sql.Date as DateField range (#15342) Change-Id: I656cc0600f929239605e17ab9cf71dc1ba96582f --- server/src/com/vaadin/ui/DateField.java | 14 +++--- .../components/datefield/DateRangeWithSqlDate.java | 54 ++++++++++++++++++++++ .../datefield/DateRangeWithSqlDateTest.java | 51 ++++++++++++++++++++ 3 files changed, 113 insertions(+), 6 deletions(-) create mode 100644 uitest/src/com/vaadin/tests/components/datefield/DateRangeWithSqlDate.java create mode 100644 uitest/src/com/vaadin/tests/components/datefield/DateRangeWithSqlDateTest.java diff --git a/server/src/com/vaadin/ui/DateField.java b/server/src/com/vaadin/ui/DateField.java index 030bd5f6c2..d5700c4b65 100644 --- a/server/src/com/vaadin/ui/DateField.java +++ b/server/src/com/vaadin/ui/DateField.java @@ -316,10 +316,10 @@ public class DateField extends AbstractField implements throw new IllegalStateException( "startDate cannot be later than endDate"); } - getState().rangeStart = startDate; - // rangeStart = startDate; - // This has to be done to correct for the resolution - // updateRangeState(); + + // Create a defensive copy against issues when using java.sql.Date (and + // also against mutable Date). + getState().rangeStart = new Date(startDate.getTime()); updateRangeValidator(); } @@ -436,8 +436,10 @@ public class DateField extends AbstractField implements throw new IllegalStateException( "endDate cannot be earlier than startDate"); } - // rangeEnd = endDate; - getState().rangeEnd = endDate; + + // Create a defensive copy against issues when using java.sql.Date (and + // also against mutable Date). + getState().rangeEnd = new Date(endDate.getTime()); updateRangeValidator(); } diff --git a/uitest/src/com/vaadin/tests/components/datefield/DateRangeWithSqlDate.java b/uitest/src/com/vaadin/tests/components/datefield/DateRangeWithSqlDate.java new file mode 100644 index 0000000000..74d3e85892 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/datefield/DateRangeWithSqlDate.java @@ -0,0 +1,54 @@ +/* + * 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.DateField; +import com.vaadin.ui.InlineDateField; + +public class DateRangeWithSqlDate extends AbstractTestUI { + + // 2014-12-01 + private static final java.sql.Date startDate = new java.sql.Date( + 1417467822699L); + + // 2014-12-02 + private static final java.sql.Date endDate = new java.sql.Date( + 1417554763317L); + + @Override + protected void setup(VaadinRequest request) { + DateField df = new InlineDateField(); + df.setRangeStart(startDate); + df.setRangeEnd(endDate); + + df.setValue(startDate); + + addComponent(df); + } + + @Override + protected String getTestDescription() { + return "Test that java.sql.Date can be given to specify date range start and end dates."; + } + + @Override + protected Integer getTicketNumber() { + return 15342; + } + +} diff --git a/uitest/src/com/vaadin/tests/components/datefield/DateRangeWithSqlDateTest.java b/uitest/src/com/vaadin/tests/components/datefield/DateRangeWithSqlDateTest.java new file mode 100644 index 0000000000..2352011585 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/datefield/DateRangeWithSqlDateTest.java @@ -0,0 +1,51 @@ +/* + * 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 static org.junit.Assert.assertEquals; + +import java.util.List; + +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; + +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class DateRangeWithSqlDateTest extends MultiBrowserTest { + + @Test + public void testDateRange() { + openTestURL(); + + // Get all cells of the inline datefield. + List cells = driver.findElements(By + .className("v-inline-datefield-calendarpanel-day")); + + // Verify the range is rendered correctly. + assertCell(cells.get(0), "30", true); + assertCell(cells.get(1), "1", false); + assertCell(cells.get(2), "2", false); + assertCell(cells.get(3), "3", true); + } + + private void assertCell(WebElement cell, String text, boolean outsideRange) { + assertEquals(text, cell.getText()); + assertEquals(outsideRange, + cell.getAttribute("class").contains("outside-range")); + } + +} -- cgit v1.2.3 From 153129d52d0167a4d54d7c133155eeec7d407a19 Mon Sep 17 00:00:00 2001 From: Alexey Fansky Date: Wed, 3 Dec 2014 17:07:43 -0800 Subject: Returning all validation errors in the exception cause when submitting a field group (#14742) Following the similar logic as in AbstractField when multiple validation errors occur. Catching all InvalidValueExceptions and putting them together wrapped into the exception. Change-Id: Ied08fd2155412539b28ef94bc74e6c989c62f709 --- .../src/com/vaadin/data/fieldgroup/FieldGroup.java | 86 ++++++++++++++++------ uitest/ivy.xml | 2 + .../tests/fieldgroup/MultipleValidationErrors.java | 72 ++++++++++++++++++ .../fieldgroup/MultipleValidationErrorsTest.java | 37 ++++++++++ .../PersonBeanWithValidationAnnotations.java | 31 ++++++++ 5 files changed, 205 insertions(+), 23 deletions(-) create mode 100644 uitest/src/com/vaadin/tests/fieldgroup/MultipleValidationErrors.java create mode 100644 uitest/src/com/vaadin/tests/fieldgroup/MultipleValidationErrorsTest.java create mode 100644 uitest/src/com/vaadin/tests/fieldgroup/PersonBeanWithValidationAnnotations.java diff --git a/server/src/com/vaadin/data/fieldgroup/FieldGroup.java b/server/src/com/vaadin/data/fieldgroup/FieldGroup.java index 5a4e877554..bcfa8395df 100644 --- a/server/src/com/vaadin/data/fieldgroup/FieldGroup.java +++ b/server/src/com/vaadin/data/fieldgroup/FieldGroup.java @@ -26,6 +26,7 @@ import java.util.List; import com.vaadin.data.Item; import com.vaadin.data.Property; +import com.vaadin.data.Validator; import com.vaadin.data.Validator.InvalidValueException; import com.vaadin.data.util.TransactionalPropertyWrapper; import com.vaadin.ui.AbstractField; @@ -452,6 +453,54 @@ public class FieldGroup implements Serializable { // Not using buffered mode, nothing to do return; } + + startTransactions(); + + try { + firePreCommitEvent(); + + List invalidValueExceptions = commitFields(); + + if(invalidValueExceptions.isEmpty()) { + firePostCommitEvent(); + commitTransactions(); + } else { + throwInvalidValueException(invalidValueExceptions); + } + } catch (Exception e) { + rollbackTransactions(); + + throw new CommitException("Commit failed", e); + } + + } + + private List commitFields() { + List invalidValueExceptions = new ArrayList(); + + for (Field f : fieldToPropertyId.keySet()) { + try { + f.commit(); + } catch (InvalidValueException e) { + invalidValueExceptions.add(e); + } + } + + return invalidValueExceptions; + } + + private void throwInvalidValueException(List invalidValueExceptions) { + if(invalidValueExceptions.size() == 1) { + throw invalidValueExceptions.get(0); + } else { + InvalidValueException[] causes = invalidValueExceptions.toArray( + new InvalidValueException[invalidValueExceptions.size()]); + + throw new InvalidValueException(null, causes); + } + } + + private void startTransactions() throws CommitException { for (Field f : fieldToPropertyId.keySet()) { Property.Transactional property = (Property.Transactional) f .getPropertyDataSource(); @@ -462,33 +511,24 @@ public class FieldGroup implements Serializable { } property.startTransaction(); } - try { - firePreCommitEvent(); - // Commit the field values to the properties - for (Field f : fieldToPropertyId.keySet()) { - f.commit(); - } - firePostCommitEvent(); + } - // Commit the properties - for (Field f : fieldToPropertyId.keySet()) { - ((Property.Transactional) f.getPropertyDataSource()) - .commit(); - } + private void commitTransactions() { + for (Field f : fieldToPropertyId.keySet()) { + ((Property.Transactional) f.getPropertyDataSource()) + .commit(); + } + } - } catch (Exception e) { - for (Field f : fieldToPropertyId.keySet()) { - try { - ((Property.Transactional) f.getPropertyDataSource()) - .rollback(); - } catch (Exception rollbackException) { - // FIXME: What to do ? - } + private void rollbackTransactions() { + for (Field f : fieldToPropertyId.keySet()) { + try { + ((Property.Transactional) f.getPropertyDataSource()) + .rollback(); + } catch (Exception rollbackException) { + // FIXME: What to do ? } - - throw new CommitException("Commit failed", e); } - } /** diff --git a/uitest/ivy.xml b/uitest/ivy.xml index 14c8bc6ce3..78171f9a9c 100644 --- a/uitest/ivy.xml +++ b/uitest/ivy.xml @@ -27,6 +27,8 @@ + diff --git a/uitest/src/com/vaadin/tests/fieldgroup/MultipleValidationErrors.java b/uitest/src/com/vaadin/tests/fieldgroup/MultipleValidationErrors.java new file mode 100644 index 0000000000..5110bf6dcf --- /dev/null +++ b/uitest/src/com/vaadin/tests/fieldgroup/MultipleValidationErrors.java @@ -0,0 +1,72 @@ +package com.vaadin.tests.fieldgroup; + +import com.vaadin.data.Validator; +import com.vaadin.data.fieldgroup.FieldGroup; +import com.vaadin.data.util.BeanItem; +import com.vaadin.data.validator.BeanValidator; +import com.vaadin.server.AbstractErrorMessage; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Button; +import com.vaadin.ui.Label; +import com.vaadin.ui.TextField; +import org.apache.commons.lang.StringEscapeUtils; + +public class MultipleValidationErrors extends AbstractTestUI { + + public static final String FIRST_NAME_NOT_NULL_VALIDATION_MESSAGE = "first name is null"; + public static final String LAST_NAME_NOT_NULL_VALIDATION_MESSAGE = "last name is null"; + public static final String FIRST_NAME_NOT_EMPTY_VALIDATION_MESSAGE = "first name is empty"; + public static final String LAST_NAME_NOT_EMPTY_VALIDATION_MESSAGE = "last name is empty"; + + @Override + protected void setup(VaadinRequest request) { + BeanItem item = new BeanItem( + new PersonBeanWithValidationAnnotations()); + final FieldGroup fieldGroup = new FieldGroup(item); + + bindTextField(item, fieldGroup, "First Name", "firstName"); + bindTextField(item, fieldGroup, "Last Name", "lastName"); + + final Label validationErrors = new Label(); + validationErrors.setId("validationErrors"); + addComponent(validationErrors); + + addButton("Submit", new Button.ClickListener() { + @Override + public void buttonClick(Button.ClickEvent event) { + validationErrors.setValue(""); + try { + fieldGroup.commit(); + } catch (FieldGroup.CommitException e) { + if (e.getCause() != null && e.getCause() instanceof Validator.InvalidValueException) { + validationErrors.setValue(StringEscapeUtils.unescapeHtml( + AbstractErrorMessage.getErrorMessageForException(e.getCause()).getFormattedHtmlMessage())); + } + } + + + } + }); + } + + private void bindTextField(BeanItem item, FieldGroup fieldGroup, + String caption, String propertyId) { + TextField textfield = new TextField(caption, item.getItemProperty(propertyId)); + textfield.addValidator(new BeanValidator(PersonBeanWithValidationAnnotations.class, propertyId)); + + fieldGroup.bind(textfield, propertyId); + + addComponent(textfield); + } + + @Override + protected Integer getTicketNumber() { + return 14742; + } + + @Override + protected String getTestDescription() { + return "All validation errors should be included when committing a field group."; + } +} diff --git a/uitest/src/com/vaadin/tests/fieldgroup/MultipleValidationErrorsTest.java b/uitest/src/com/vaadin/tests/fieldgroup/MultipleValidationErrorsTest.java new file mode 100644 index 0000000000..175b650be6 --- /dev/null +++ b/uitest/src/com/vaadin/tests/fieldgroup/MultipleValidationErrorsTest.java @@ -0,0 +1,37 @@ +package com.vaadin.tests.fieldgroup; + +import com.vaadin.testbench.elements.ButtonElement; +import com.vaadin.testbench.elements.LabelElement; +import com.vaadin.testbench.elements.TextFieldElement; +import com.vaadin.tests.tb3.MultiBrowserTest; +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.MatcherAssert.assertThat; + +public class MultipleValidationErrorsTest extends MultiBrowserTest { + + private void commitTextFields() { + $(ButtonElement.class).caption("Submit").first().click(); + } + + private void clearTextField(String caption) { + TextFieldElement textField = $(TextFieldElement.class).caption(caption).first(); + textField.clear(); + } + + @Test + public void validationErrorsIncludeBothErrors() { + openTestURL(); + + clearTextField("First Name"); + clearTextField("Last Name"); + + commitTextFields(); + + String validationErrors = $(LabelElement.class).id("validationErrors").getText(); + + assertThat(validationErrors, containsString(MultipleValidationErrors.FIRST_NAME_NOT_EMPTY_VALIDATION_MESSAGE)); + assertThat(validationErrors, containsString(MultipleValidationErrors.LAST_NAME_NOT_EMPTY_VALIDATION_MESSAGE)); + } +} diff --git a/uitest/src/com/vaadin/tests/fieldgroup/PersonBeanWithValidationAnnotations.java b/uitest/src/com/vaadin/tests/fieldgroup/PersonBeanWithValidationAnnotations.java new file mode 100644 index 0000000000..5f81ee248b --- /dev/null +++ b/uitest/src/com/vaadin/tests/fieldgroup/PersonBeanWithValidationAnnotations.java @@ -0,0 +1,31 @@ +package com.vaadin.tests.fieldgroup; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import java.io.Serializable; + +public class PersonBeanWithValidationAnnotations implements Serializable { + @NotNull(message = MultipleValidationErrors.FIRST_NAME_NOT_NULL_VALIDATION_MESSAGE) + @Size(min = 1, message = MultipleValidationErrors.FIRST_NAME_NOT_EMPTY_VALIDATION_MESSAGE) + private String firstName; + + @NotNull(message = MultipleValidationErrors.LAST_NAME_NOT_NULL_VALIDATION_MESSAGE) + @Size(min = 1, message = MultipleValidationErrors.LAST_NAME_NOT_EMPTY_VALIDATION_MESSAGE) + private String lastName; + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } +} \ No newline at end of file -- cgit v1.2.3 From b579c8aa1f116613bc2c54a562619dbba0608a5d Mon Sep 17 00:00:00 2001 From: Sauli Tähkäpää Date: Tue, 9 Dec 2014 15:29:10 +0200 Subject: Add link to IDEA workspace instructions. Change-Id: I85c2b537926f887bce0e0b370b6be6b56c038053 --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 45421f3fbd..2f972ebefa 100644 --- a/README.md +++ b/README.md @@ -104,10 +104,15 @@ Note that the included Vaadin TestBench (browser) tests require access to a Test Building a package ===== -The distribution files can be built in two steps. +The distribution files can be built in two steps. 1. Unpack required gwt jars into the project
ant -f gwt-files.xml unpack.gwt
2. Build the project by running
ant
in the project root directory (add -Dvaadin.version=1.2.3 to use a specific version number). + +Setting up other IDEs to Develop Vaadin 7 +========= +- Unofficial instructions + - IntelliJ IDEA: http://github.com/Saulis/vaadin-idea-workspace/ -- cgit v1.2.3 From 52fb8cd941caad7d9c6277d7b154498abb6b99e6 Mon Sep 17 00:00:00 2001 From: Artur Signell Date: Tue, 9 Dec 2014 21:18:50 +0200 Subject: Make addNestedContainerBean work with existing bean item container items (#15355) Change-Id: I04a91349fce452aa26c304413061440ad74465cc --- .../vaadin/data/util/AbstractBeanContainer.java | 2 +- .../vaadin/data/util/BeanItemContainerTest.java | 42 ++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/server/src/com/vaadin/data/util/AbstractBeanContainer.java b/server/src/com/vaadin/data/util/AbstractBeanContainer.java index 7d7d71c423..fad0934e53 100644 --- a/server/src/com/vaadin/data/util/AbstractBeanContainer.java +++ b/server/src/com/vaadin/data/util/AbstractBeanContainer.java @@ -880,7 +880,7 @@ public abstract class AbstractBeanContainer extends model.put(qualifiedPropertyId, pd); model.remove(propertyId); for (BeanItem item : itemIdToItem.values()) { - item.addItemProperty(propertyId, + item.addItemProperty(qualifiedPropertyId, pd.createProperty(item.getBean())); item.removeItemProperty(propertyId); } diff --git a/server/tests/src/com/vaadin/data/util/BeanItemContainerTest.java b/server/tests/src/com/vaadin/data/util/BeanItemContainerTest.java index 01c282e294..514bf70416 100644 --- a/server/tests/src/com/vaadin/data/util/BeanItemContainerTest.java +++ b/server/tests/src/com/vaadin/data/util/BeanItemContainerTest.java @@ -12,6 +12,7 @@ import org.junit.Assert; import com.vaadin.data.Container; import com.vaadin.data.Item; +import com.vaadin.data.util.NestedMethodPropertyTest.Address; /** * Test basic functionality of BeanItemContainer. @@ -740,4 +741,45 @@ public class BeanItemContainerTest extends AbstractBeanContainerTest { assertNull(container.getContainerProperty(john, "address.street") .getValue()); } + + public void testAddNestedContainerBeanBeforeData() { + BeanItemContainer container = new BeanItemContainer( + NestedMethodPropertyTest.Person.class); + + container.addNestedContainerBean("address"); + + assertTrue(container.getContainerPropertyIds().contains( + "address.street")); + + NestedMethodPropertyTest.Person john = new NestedMethodPropertyTest.Person( + "John", new Address("streetname", 12345)); + container.addBean(john); + + assertTrue(container.getItem(john).getItemPropertyIds() + .contains("address.street")); + assertEquals("streetname", + container.getItem(john).getItemProperty("address.street") + .getValue()); + + } + + public void testAddNestedContainerBeanAfterData() { + BeanItemContainer container = new BeanItemContainer( + NestedMethodPropertyTest.Person.class); + + NestedMethodPropertyTest.Person john = new NestedMethodPropertyTest.Person( + "John", new Address("streetname", 12345)); + container.addBean(john); + + container.addNestedContainerBean("address"); + + assertTrue(container.getContainerPropertyIds().contains( + "address.street")); + assertTrue(container.getItem(john).getItemPropertyIds() + .contains("address.street")); + assertEquals("streetname", + container.getItem(john).getItemProperty("address.street") + .getValue()); + + } } -- cgit v1.2.3 From f1f089772749a0eb4b031f1876b95bb684565473 Mon Sep 17 00:00:00 2001 From: Artur Signell Date: Tue, 2 Dec 2014 21:19:06 +0200 Subject: Make TextField.clear() use empty string to be consistent with constructor (#14755) Change-Id: Ic50ff305277e54f50353fc948b31a89a6b3e083f --- server/src/com/vaadin/ui/TextField.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/server/src/com/vaadin/ui/TextField.java b/server/src/com/vaadin/ui/TextField.java index fb1e4284a2..1fc10c6ced 100644 --- a/server/src/com/vaadin/ui/TextField.java +++ b/server/src/com/vaadin/ui/TextField.java @@ -43,7 +43,7 @@ public class TextField extends AbstractTextField { * Constructs an empty TextField with no caption. */ public TextField() { - setValue(""); + clear(); } /** @@ -99,4 +99,14 @@ public class TextField extends AbstractTextField { setCaption(caption); } + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.AbstractField#clear() + */ + @Override + public void clear() { + setValue(""); + } + } -- cgit v1.2.3 From 8ce89592362226d6687e1267632a75d85774b67d Mon Sep 17 00:00:00 2001 From: Denis Anisimov Date: Thu, 6 Nov 2014 09:15:15 +0200 Subject: Position calendar popup on the left side if there is no space (#14757). Change-Id: I83836bbf077033712a569c8eff52576b012b4dee --- .../VAADIN/themes/valo/components/_datefield.scss | 2 + .../src/com/vaadin/client/ui/VPopupCalendar.java | 165 ++++++++++++++------- .../datefield/DateFieldPopupPosition.java | 52 +++++++ .../datefield/DateFieldPopupPositionTest.java | 68 +++++++++ .../datefield/DefaultDateFieldPopupPosition.java | 27 ++++ .../DefaultDateFieldPopupPositionTest.java | 43 ++++++ .../datefield/ValoDateFieldPopupPosition.java | 30 ++++ .../datefield/ValoDateFieldPopupPositionTest.java | 43 ++++++ 8 files changed, 376 insertions(+), 54 deletions(-) create mode 100644 uitest/src/com/vaadin/tests/components/datefield/DateFieldPopupPosition.java create mode 100644 uitest/src/com/vaadin/tests/components/datefield/DateFieldPopupPositionTest.java create mode 100644 uitest/src/com/vaadin/tests/components/datefield/DefaultDateFieldPopupPosition.java create mode 100644 uitest/src/com/vaadin/tests/components/datefield/DefaultDateFieldPopupPositionTest.java create mode 100644 uitest/src/com/vaadin/tests/components/datefield/ValoDateFieldPopupPosition.java create mode 100644 uitest/src/com/vaadin/tests/components/datefield/ValoDateFieldPopupPositionTest.java 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); + } +} -- cgit v1.2.3 From e1b335022ec359061e696cf2711327663267e704 Mon Sep 17 00:00:00 2001 From: Sergey Budkin Date: Tue, 25 Nov 2014 19:35:13 +0200 Subject: Long events aren't displayed properly when using Container (#15242) Rewrote event selection. Change-Id: I8f0dd1c5ec736ea14037619b1656a79b7e3532be --- .../calendar/ContainerEventProvider.java | 74 ++++++---------------- .../calendar/BeanItemContainerLongEventTest.java | 55 ++++++++++++++++ .../calendar/BeanItemContainerTestUI.java | 8 +++ 3 files changed, 81 insertions(+), 56 deletions(-) create mode 100644 uitest/src/com/vaadin/tests/components/calendar/BeanItemContainerLongEventTest.java diff --git a/server/src/com/vaadin/ui/components/calendar/ContainerEventProvider.java b/server/src/com/vaadin/ui/components/calendar/ContainerEventProvider.java index fcaabcc079..2f51b21f7c 100644 --- a/server/src/com/vaadin/ui/components/calendar/ContainerEventProvider.java +++ b/server/src/com/vaadin/ui/components/calendar/ContainerEventProvider.java @@ -249,71 +249,33 @@ public class ContainerEventProvider implements CalendarEditableEventProvider, @Override public List getEvents(Date startDate, Date endDate) { eventCache.clear(); - - int[] rangeIndexes = getFirstAndLastEventIndex(startDate, endDate); - for (int i = rangeIndexes[0]; i <= rangeIndexes[1] - && i < container.size(); i++) { - eventCache.add(getEvent(i)); - } - return Collections.unmodifiableList(eventCache); - } - - /** - * Get the first event for a date - * - * @param date - * The date to search for, NUll returns first event in container - * @return Returns an array where the first item is the start index and the - * second item is the end item - */ - private int[] getFirstAndLastEventIndex(Date start, Date end) { - int startIndex = 0; int size = container.size(); assert size >= 0; - int endIndex = size - 1; - if (start != null) { - /* - * Iterating from the start of the container, if range is in the end - * of the container then this will be slow TODO This could be - * improved by using some sort of divide and conquer algorithm - */ - while (startIndex < size) { - Object id = container.getIdByIndex(startIndex); - Item item = container.getItem(id); - Date d = (Date) item.getItemProperty(startDateProperty) + for (int i = 0; i < size; i++) { + Object id = container.getIdByIndex(i); + Item item = container.getItem(id); + boolean add = true; + if (startDate != null) { + Date eventEnd = (Date) item.getItemProperty(endDateProperty) .getValue(); - if (d.compareTo(start) >= 0) { - break; + if (eventEnd.compareTo(startDate) < 0) { + add = false; } - startIndex++; } - } - - if (end != null) { - /* - * Iterate from the start index until range ends - */ - endIndex = startIndex; - while (endIndex < size - 1) { - Object id = container.getIdByIndex(endIndex); - Item item = container.getItem(id); - Date d = (Date) item.getItemProperty(endDateProperty) - .getValue(); - if (d == null) { - // No end date present, use start date - d = (Date) item.getItemProperty(startDateProperty) - .getValue(); + if (add && endDate != null) { + Date eventStart = (Date) item + .getItemProperty(startDateProperty).getValue(); + if (eventStart.compareTo(endDate) >= 0) { + break; // because container is sorted, all further events + // will be even later } - if (d.compareTo(end) >= 0) { - endIndex--; - break; - } - endIndex++; + } + if (add) { + eventCache.add(getEvent(i)); } } - - return new int[] { startIndex, endIndex }; + return Collections.unmodifiableList(eventCache); } /* diff --git a/uitest/src/com/vaadin/tests/components/calendar/BeanItemContainerLongEventTest.java b/uitest/src/com/vaadin/tests/components/calendar/BeanItemContainerLongEventTest.java new file mode 100644 index 0000000000..0ec196f266 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/calendar/BeanItemContainerLongEventTest.java @@ -0,0 +1,55 @@ +/* + * 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.calendar; + +import org.junit.Assert; +import org.junit.Test; +import org.openqa.selenium.WebElement; + +import com.vaadin.testbench.By; +import com.vaadin.tests.tb3.MultiBrowserTest; + +/** + * Tests if long event which began before the view period is shown (#15242) + */ +public class BeanItemContainerLongEventTest extends MultiBrowserTest { + + @Override + protected String getDeploymentPath() { + return "/run/BeanItemContainerTestUI?restartApplication"; + } + + @Test + public void testEventDisplayedInWeekView() { + openTestURL(); + WebElement target = driver.findElements( + By.className("v-calendar-week-number")).get(1); + target.click(); + target = driver.findElement(By.className("v-calendar-event")); + Assert.assertEquals("Wrong event name", "Long event", target.getText()); + } + + @Test + public void testEventDisplayedInDayView() { + openTestURL(); + WebElement target = driver.findElements( + By.className("v-calendar-day-number")).get(5); + target.click(); + target = driver.findElement(By.className("v-calendar-event")); + Assert.assertEquals("Wrong event name", "Long event", target.getText()); + } + +} diff --git a/uitest/src/com/vaadin/tests/components/calendar/BeanItemContainerTestUI.java b/uitest/src/com/vaadin/tests/components/calendar/BeanItemContainerTestUI.java index 83fc4a03cb..bda3b34875 100644 --- a/uitest/src/com/vaadin/tests/components/calendar/BeanItemContainerTestUI.java +++ b/uitest/src/com/vaadin/tests/components/calendar/BeanItemContainerTestUI.java @@ -17,6 +17,7 @@ package com.vaadin.tests.components.calendar; import java.util.Arrays; import java.util.Date; +import java.util.GregorianCalendar; import com.vaadin.data.fieldgroup.FieldGroup; import com.vaadin.data.fieldgroup.FieldGroup.CommitException; @@ -69,6 +70,13 @@ public class BeanItemContainerTestUI extends UI { table.setVisibleColumns(new Object[] { "caption", "description", "start", "end" }); content.addComponent(table); + + BasicEvent longEvent = new BasicEvent(); + longEvent.setCaption("Long event"); + longEvent.setAllDay(true); + longEvent.setStart(new GregorianCalendar(2000, 1, 3).getTime()); + longEvent.setEnd(new GregorianCalendar(2000, 2, 5).getTime()); + events.addBean(longEvent); } /** -- cgit v1.2.3 From 98d0eec7bb50e4af342358d2aa2ed80fe4564c85 Mon Sep 17 00:00:00 2001 From: Jouni Koivuviita Date: Wed, 10 Dec 2014 10:42:37 +0200 Subject: MenuBar submenus close unexpectedly if use Valo theme (#15255) This fix is for animation-in and animation-out. Fix was done in VMenuBar. VOverlay provides now hide() method with possibility to enable/disable the animations via parameters boolean animateIn, boolean animateOut. By default they are true (not to break animate-in, animate-out for VWindow and VNotification) Change-Id: I49981952c7c18a02edd7fa9e6d247bb18f660207 --- .../VAADIN/themes/valo/components/_menubar.scss | 4 - client/src/com/vaadin/client/ui/VMenuBar.java | 28 +++++-- client/src/com/vaadin/client/ui/VOverlay.java | 91 ++++++++++++++-------- .../menubar/MenuBarSubmenusClosingValo.java | 60 ++++++++++++++ .../menubar/MenuBarSubmenusClosingValoTest.java | 72 +++++++++++++++++ 5 files changed, 212 insertions(+), 43 deletions(-) create mode 100644 uitest/src/com/vaadin/tests/components/menubar/MenuBarSubmenusClosingValo.java create mode 100644 uitest/src/com/vaadin/tests/components/menubar/MenuBarSubmenusClosingValoTest.java diff --git a/WebContent/VAADIN/themes/valo/components/_menubar.scss b/WebContent/VAADIN/themes/valo/components/_menubar.scss index 9075eb7ba8..0f9c61d2ce 100644 --- a/WebContent/VAADIN/themes/valo/components/_menubar.scss +++ b/WebContent/VAADIN/themes/valo/components/_menubar.scss @@ -70,10 +70,6 @@ .#{$primary-stylename}-popup { @include valo-menubar-popup-style($primary-stylename); - - &.#{$primary-stylename}-popup-animate-out { - @include animation(none); - } } diff --git a/client/src/com/vaadin/client/ui/VMenuBar.java b/client/src/com/vaadin/client/ui/VMenuBar.java index 5102e6faea..66160e691d 100644 --- a/client/src/com/vaadin/client/ui/VMenuBar.java +++ b/client/src/com/vaadin/client/ui/VMenuBar.java @@ -476,7 +476,8 @@ public class VMenuBar extends SimpleFocusablePanel implements if (menuVisible && visibleChildMenu != item.getSubMenu() && popup != null) { - popup.hide(); + // #15255 - disable animation-in/out when hide in this case + popup.hide(false, false, false); } if (menuVisible && item.getSubMenu() != null @@ -707,9 +708,22 @@ public class VMenuBar extends SimpleFocusablePanel implements * Recursively hide all child menus */ public void hideChildren() { + hideChildren(true, true); + } + + /** + * + * Recursively hide all child menus + * + * @param animateIn + * enable/disable animate-in animation when hide popup + * @param animateOut + * enable/disable animate-out animation when hide popup + */ + public void hideChildren(boolean animateIn, boolean animateOut) { if (visibleChildMenu != null) { - visibleChildMenu.hideChildren(); - popup.hide(); + visibleChildMenu.hideChildren(animateIn, animateOut); + popup.hide(false, animateIn, animateOut); } } @@ -1326,7 +1340,8 @@ public class VMenuBar extends SimpleFocusablePanel implements VMenuBar root = getParentMenu(); root.getSelected().getSubMenu().setSelected(null); - root.hideChildren(); + // #15255 - disable animate-in/out when hide popup + root.hideChildren(false, false); // Get the root menus items and select the previous one int idx = root.getItems().indexOf(root.getSelected()); @@ -1383,8 +1398,9 @@ public class VMenuBar extends SimpleFocusablePanel implements root = root.getParentMenu(); } - // Hide the submenu - root.hideChildren(); + // Hide the submenu (#15255 - disable animate-in/out when hide + // popup) + root.hideChildren(false, false); // Get the root menus items and select the next one int idx = root.getItems().indexOf(root.getSelected()); diff --git a/client/src/com/vaadin/client/ui/VOverlay.java b/client/src/com/vaadin/client/ui/VOverlay.java index dfd81faf94..9071b6ee47 100644 --- a/client/src/com/vaadin/client/ui/VOverlay.java +++ b/client/src/com/vaadin/client/ui/VOverlay.java @@ -51,7 +51,7 @@ import com.vaadin.client.Util; * temporary float over other components like context menus etc. This is to deal * stacking order correctly with VWindow objects. *

- * + * *

Shadow

*

* The separate shadow element underneath the main overlay element is @@ -62,7 +62,7 @@ import com.vaadin.client.Util; * supports, add -webkit-box-shadow and the standard * box-shadow properties. *

- * + * *

* For IE8, which doesn't support CSS box-shadow, you can use the proprietary * DropShadow filter. It doesn't provide the exact same features as box-shadow, @@ -70,7 +70,7 @@ import com.vaadin.client.Util; * border or a pseudo-element underneath the overlay which mimics a shadow, or * any combination of these. *

- * + * *

* Read more about the DropShadow filter from { * Shadow element style. If an extending class wishes to use a different * style of shadow, it can use setShadowStyle(String) to give the shadow * element a new style name. - * + * * @deprecated See main JavaDoc for VOverlay */ @Deprecated @@ -187,9 +187,9 @@ public class VOverlay extends PopupPanel implements CloseHandler { /** * The shadow element for this overlay. - * + * * @deprecated See main JavaDoc for VOverlay - * + * */ @Deprecated private Element shadow; @@ -218,7 +218,7 @@ public class VOverlay extends PopupPanel implements CloseHandler { /** * The HTML snippet that is used to render the actual shadow. In consists of * nine different DIV-elements with the following class names: - * + * *

      *   .v-shadow[-stylename]
      *   ----------------------------------------------
@@ -231,9 +231,9 @@ public class VOverlay extends PopupPanel implements CloseHandler {
      *   | .bottom-left  |  .bottom  |  .bottom-right |
      *   ----------------------------------------------
      * 
- * + * * See default theme 'shadow.css' for implementation example. - * + * * @deprecated See main JavaDoc for VOverlay */ @Deprecated @@ -280,7 +280,7 @@ public class VOverlay extends PopupPanel implements CloseHandler { * Return true if a separate shadow div should be used. Since Vaadin 7.3, * shadows are implemented with CSS box-shadow. Thus, a shadow div is only * used for IE8 by default. - * + * * @deprecated See main JavaDoc for VOverlay * @since 7.3 * @return true to use a shadow div @@ -294,10 +294,10 @@ public class VOverlay extends PopupPanel implements CloseHandler { * Method to control whether DOM elements for shadow are added. With this * method subclasses can control displaying of shadow also after the * constructor. - * + * * @param enabled * true if shadow should be displayed - * + * * @deprecated See main JavaDoc for VOverlay */ @Deprecated @@ -361,7 +361,7 @@ public class VOverlay extends PopupPanel implements CloseHandler { /** * Set the z-index (visual stack position) for this overlay. - * + * * @param zIndex * The new z-index */ @@ -574,12 +574,12 @@ public class VOverlay extends PopupPanel implements CloseHandler { * Sets the shadow style for this overlay. Will override any previous style * for the shadow. The default style name is defined by CLASSNAME_SHADOW. * The given style will be prefixed with CLASSNAME_SHADOW. - * + * * @param style * The new style name for the shadow element. Will be prefixed by * CLASSNAME_SHADOW, e.g. style=='foobar' -> actual style * name=='v-shadow-foobar'. - * + * * @deprecated See main JavaDoc for VOverlay */ @Deprecated @@ -593,7 +593,7 @@ public class VOverlay extends PopupPanel implements CloseHandler { * Extending classes should always call this method after they change the * size of overlay without using normal 'setWidth(String)' and * 'setHeight(String)' methods (if not calling super.setWidth/Height). - * + * */ public void positionOrSizeUpdated() { positionOrSizeUpdated(1.0); @@ -612,7 +612,7 @@ public class VOverlay extends PopupPanel implements CloseHandler { * elements. Can be used to animate the related elements, using the * 'progress' parameter (used to animate the shadow in sync with GWT * PopupPanel's default animation 'PopupPanel.AnimationType.CENTER'). - * + * * @param progress * A value between 0.0 and 1.0, indicating the progress of the * animation (0=start, 1=end). @@ -721,7 +721,7 @@ public class VOverlay extends PopupPanel implements CloseHandler { * Returns true if we should add a shim iframe below the overlay to deal * with zindex issues with PDFs and applets. Can be overriden to disable * shim iframes if they are not needed. - * + * * @return true if a shim iframe should be added, false otherwise */ protected boolean needsShimElement() { @@ -783,13 +783,13 @@ public class VOverlay extends PopupPanel implements CloseHandler { /** * Enables or disables sinking the events of the shadow to the same * onBrowserEvent as events to the actual overlay goes. - * + * * Please note, that if you enable this, you can't assume that e.g. * event.getEventTarget returns an element inside the DOM structure of the * overlay - * + * * @param sinkShadowEvents - * + * * @deprecated See main JavaDoc for VOverlay */ @Deprecated @@ -813,7 +813,7 @@ public class VOverlay extends PopupPanel implements CloseHandler { /** * Get owner (Widget that made this VOverlay, not the layout parent) of * VOverlay - * + * * @return Owner (creator) or null if not defined */ public Widget getOwner() { @@ -823,7 +823,7 @@ public class VOverlay extends PopupPanel implements CloseHandler { /** * Set owner (Widget that made this VOverlay, not the layout parent) of * VOverlay - * + * * @param owner * Owner (creator) of VOverlay */ @@ -834,7 +834,7 @@ public class VOverlay extends PopupPanel implements CloseHandler { /** * Get the {@link ApplicationConnection} that this overlay belongs to. If * it's not set, {@link #getOwner()} is used to figure it out. - * + * * @return */ protected ApplicationConnection getApplicationConnection() { @@ -854,7 +854,7 @@ public class VOverlay extends PopupPanel implements CloseHandler { /** * Gets the 'overlay container' element. Tries to find the current * {@link ApplicationConnection} using {@link #getApplicationConnection()}. - * + * * @return the overlay container element for the current * {@link ApplicationConnection} or another element if the current * {@link ApplicationConnection} cannot be determined. @@ -878,7 +878,7 @@ public class VOverlay extends PopupPanel implements CloseHandler { * {@link ApplicationConnection}. Each overlay should be created in a * overlay container element, so that the correct theme and styles can be * applied. - * + * * @param ac * A reference to {@link ApplicationConnection} * @return The overlay container @@ -905,7 +905,7 @@ public class VOverlay extends PopupPanel implements CloseHandler { /** * Set the label of the container element, where tooltip, notification and * dialgs are added to. - * + * * @param applicationConnection * the application connection for which to change the label * @param overlayContainerLabel @@ -938,10 +938,10 @@ public class VOverlay extends PopupPanel implements CloseHandler { /** * Gets the visual viewport width, which is useful for e.g iOS where the * view can be zoomed in while keeping the layout viewport intact. - * + * * Falls back to layout viewport; for those browsers/devices the difference * is that the scrollbar with is included (if there is a scrollbar). - * + * * @since 7.0.7 * @return */ @@ -957,10 +957,10 @@ public class VOverlay extends PopupPanel implements CloseHandler { /** * Gets the visual viewport height, which is useful for e.g iOS where the * view can be zoomed in while keeping the layout viewport intact. - * + * * Falls back to layout viewport; for those browsers/devices the difference * is that the scrollbar with is included (if there is a scrollbar). - * + * * @since 7.0.7 * @return */ @@ -1000,10 +1000,33 @@ public class VOverlay extends PopupPanel implements CloseHandler { */ @Override public void hide(final boolean autoClosed) { + hide(autoClosed, true, true); + } + + /** + * + * Hides the popup and detaches it from the page. This has no effect if it + * is not currently showing. Animation-in, animation-out can be + * enable/disabled for different use cases. + * + * @see com.google.gwt.user.client.ui.PopupPanel#hide(boolean) + * + * @param autoClosed + * the value that will be passed to + * {@link CloseHandler#onClose(CloseEvent)} when the popup is + * closed + * @param animateIn + * enable/disable animate-in animation + * @param animateOut + * enable/disable animate-out animation + */ + public void hide(final boolean autoClosed, final boolean animateIn, + final boolean animateOut) { if (BrowserInfo.get().isIE8() || BrowserInfo.get().isIE9()) { super.hide(autoClosed); } else { - if (getStyleName().contains(ADDITIONAL_CLASSNAME_ANIMATE_IN)) { + if (animateIn + && getStyleName().contains(ADDITIONAL_CLASSNAME_ANIMATE_IN)) { AnimationUtil.addAnimationEndListener(getElement(), new AnimationEndListener() { @Override @@ -1029,7 +1052,9 @@ public class VOverlay extends PopupPanel implements CloseHandler { animationName = ""; } - if (animationName.contains(ADDITIONAL_CLASSNAME_ANIMATE_OUT)) { + if (animateOut + && animationName + .contains(ADDITIONAL_CLASSNAME_ANIMATE_OUT)) { // Disable GWT PopupPanel closing animation if used setAnimationEnabled(false); diff --git a/uitest/src/com/vaadin/tests/components/menubar/MenuBarSubmenusClosingValo.java b/uitest/src/com/vaadin/tests/components/menubar/MenuBarSubmenusClosingValo.java new file mode 100644 index 0000000000..88d89abbf4 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/menubar/MenuBarSubmenusClosingValo.java @@ -0,0 +1,60 @@ +package com.vaadin.tests.components.menubar; + +import com.vaadin.annotations.Theme; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.MenuBar; +import com.vaadin.ui.MenuBar.MenuItem; + +@Theme("valo") +public class MenuBarSubmenusClosingValo extends AbstractTestUI { + + private MenuItem edit; + private MenuItem file; + private MenuItem help; + + @Override + protected String getTestDescription() { + return "Tests that when moving mouse fast over menu items " + + "previous submenu popup closes before new submenu popup opens"; + } + + @Override + protected Integer getTicketNumber() { + return 15255; + } + + @Override + protected void setup(VaadinRequest request) { + // here we increase animation time to 1 second for to do auto testing + // possible + getPage().getStyles().add( + ".valo .v-menubar-popup[class*=\"animate-in\"] {" + + "-webkit-animation: valo-overlay-animate-in 1000ms; " + + "-moz-animation: valo-overlay-animate-in 1000ms; " + + "animation: valo-overlay-animate-in 1000ms;};"); + + getPage().getStyles().add( + ".valo .v-menubar-popup[class*=\"animate-out\"] {" + + "-webkit-animation: valo-animate-out-fade 1000ms; " + + "-moz-animation: valo-animate-out-fade 1000ms; " + + "animation: valo-animate-out-fade 1000ms;};"); + + MenuBar mb = new MenuBar(); + file = mb.addItem("File", null); + file.addItem("File1", null); + file.addItem("File2", null); + file.addItem("File3", null); + edit = mb.addItem("Edit", null); + edit.addItem("Edit1", null); + edit.addItem("Edit2", null); + edit.addItem("Edit3", null); + help = mb.addItem("Help", null); + help.addItem("Help1", null); + help.addItem("Help2", null); + MenuItem helpMenuItem = help.addItem("Help3", null); + helpMenuItem.addItem("SubHelp3", null); + + addComponent(mb); + } +} diff --git a/uitest/src/com/vaadin/tests/components/menubar/MenuBarSubmenusClosingValoTest.java b/uitest/src/com/vaadin/tests/components/menubar/MenuBarSubmenusClosingValoTest.java new file mode 100644 index 0000000000..b7ed88c9ca --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/menubar/MenuBarSubmenusClosingValoTest.java @@ -0,0 +1,72 @@ +/* + * 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.menubar; + +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; +import org.openqa.selenium.Keys; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.interactions.HasInputDevices; +import org.openqa.selenium.interactions.Mouse; +import org.openqa.selenium.internal.Locatable; + +import com.vaadin.testbench.By; +import com.vaadin.testbench.elements.MenuBarElement; +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class MenuBarSubmenusClosingValoTest extends MultiBrowserTest { + + @Test + public void testEnableParentLayoutControlByKeyboard() { + openTestURL(); + + MenuBarElement menu = $(MenuBarElement.class).get(0); + menu.focus(); + menu.sendKeys(Keys.SPACE); + menu.sendKeys(Keys.DOWN); + + waitForElementPresent(By.className("v-menubar-popup")); + + menu.sendKeys(Keys.ARROW_RIGHT); + menu.sendKeys(Keys.ARROW_RIGHT); + + int count = driver.findElements(By.className("v-menubar-popup")).size(); + Assert.assertTrue("The count of open popups should be one", count == 1); + } + + @Test + public void testEnableParentLayoutControlByMouse() { + openTestURL(); + + Mouse mouse = ((HasInputDevices) getDriver()).getMouse(); + + List menuItemList = driver.findElements(By + .className("v-menubar-menuitem")); + + mouse.click(((Locatable) menuItemList.get(0)).getCoordinates()); + waitForElementPresent(By.className("v-menubar-popup")); + + mouse.mouseMove(((Locatable) menuItemList.get(1)).getCoordinates()); + mouse.mouseMove(((Locatable) menuItemList.get(2)).getCoordinates()); + + waitForElementPresent(By.className("v-menubar-popup")); + + int count = driver.findElements(By.className("v-menubar-popup")).size(); + Assert.assertTrue("The count of open popups should be one", count == 1); + } +} -- cgit v1.2.3 From f0d0b207114bc373f643c961546cfe543626057b Mon Sep 17 00:00:00 2001 From: Anna Miroshnik Date: Mon, 17 Nov 2014 17:33:54 +0300 Subject: DateField popup doesn't close when popup button is clicked (#14857) Change-Id: Ieb6ff2f072726fe8707d3cce61569dd623b13ebd --- .../src/com/vaadin/client/ui/VPopupCalendar.java | 51 +++++++++++--- .../datefield/DateFieldPopupClosing.java | 43 ++++++++++++ .../datefield/DateFieldPopupClosingTest.java | 81 ++++++++++++++++++++++ 3 files changed, 165 insertions(+), 10 deletions(-) create mode 100644 uitest/src/com/vaadin/tests/components/datefield/DateFieldPopupClosing.java create mode 100644 uitest/src/com/vaadin/tests/components/datefield/DateFieldPopupClosingTest.java diff --git a/client/src/com/vaadin/client/ui/VPopupCalendar.java b/client/src/com/vaadin/client/ui/VPopupCalendar.java index 15302f0784..cf88ceb8d6 100644 --- a/client/src/com/vaadin/client/ui/VPopupCalendar.java +++ b/client/src/com/vaadin/client/ui/VPopupCalendar.java @@ -27,6 +27,10 @@ import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.DomEvent; import com.google.gwt.event.dom.client.KeyCodes; +import com.google.gwt.event.dom.client.MouseOutEvent; +import com.google.gwt.event.dom.client.MouseOutHandler; +import com.google.gwt.event.dom.client.MouseOverEvent; +import com.google.gwt.event.dom.client.MouseOverHandler; import com.google.gwt.event.logical.shared.CloseEvent; import com.google.gwt.event.logical.shared.CloseHandler; import com.google.gwt.i18n.client.DateTimeFormat; @@ -77,6 +81,15 @@ public class VPopupCalendar extends VTextualDate implements Field, private boolean open = false; + /* + * #14857: If calendarToggle button is clicked when calendar popup is + * already open we should prevent calling openCalendarPanel() in onClick, + * since we don't want to reopen it again right after it closes. + */ + private boolean preventOpenPopupCalendar = false; + private boolean cursorOverCalendarToggleButton = false; + private boolean toggleButtonClosesWithGuarantee = false; + private boolean textFieldEnabled = true; private String captionId; @@ -90,6 +103,21 @@ public class VPopupCalendar extends VTextualDate implements Field, calendarToggle.setText(""); calendarToggle.addClickHandler(this); + + calendarToggle.addDomHandler(new MouseOverHandler() { + @Override + public void onMouseOver(MouseOverEvent event) { + cursorOverCalendarToggleButton = true; + } + }, MouseOverEvent.getType()); + + calendarToggle.addDomHandler(new MouseOutHandler() { + @Override + public void onMouseOut(MouseOutEvent event) { + cursorOverCalendarToggleButton = false; + } + }, MouseOutEvent.getType()); + // -2 instead of -1 to avoid FocusWidget.onAttach to reset it calendarToggle.getElement().setTabIndex(-2); @@ -389,7 +417,10 @@ public class VPopupCalendar extends VTextualDate implements Field, @Override public void onClick(ClickEvent event) { if (event.getSource() == calendarToggle && isEnabled()) { - openCalendarPanel(); + if (!preventOpenPopupCalendar) { + openCalendarPanel(); + } + preventOpenPopupCalendar = false; } } @@ -412,15 +443,14 @@ public class VPopupCalendar extends VTextualDate implements Field, focus(); } - // TODO resolve what the "Sigh." is all about and document it here - // Sigh. - Timer t = new Timer() { - @Override - public void run() { - open = false; - } - }; - t.schedule(100); + open = false; + + if (cursorOverCalendarToggleButton + && !toggleButtonClosesWithGuarantee) { + preventOpenPopupCalendar = true; + } + + toggleButtonClosesWithGuarantee = false; } } @@ -520,6 +550,7 @@ public class VPopupCalendar extends VTextualDate implements Field, */ public void closeCalendarPanel() { if (open) { + toggleButtonClosesWithGuarantee = true; popup.hide(true); } } diff --git a/uitest/src/com/vaadin/tests/components/datefield/DateFieldPopupClosing.java b/uitest/src/com/vaadin/tests/components/datefield/DateFieldPopupClosing.java new file mode 100644 index 0000000000..60508a30d4 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/datefield/DateFieldPopupClosing.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 com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.DateField; + +public class DateFieldPopupClosing extends AbstractTestUI { + + static final String DATEFIELD_ID = "datefield"; + + @Override + protected void setup(VaadinRequest request) { + final DateField df = new DateField(); + df.setId(DATEFIELD_ID); + addComponent(df); + } + + @Override + protected String getTestDescription() { + return "DateField popup should be closed when click on popup button"; + } + + @Override + protected Integer getTicketNumber() { + return 14857; + } + +} diff --git a/uitest/src/com/vaadin/tests/components/datefield/DateFieldPopupClosingTest.java b/uitest/src/com/vaadin/tests/components/datefield/DateFieldPopupClosingTest.java new file mode 100644 index 0000000000..9fd6fe82e2 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/datefield/DateFieldPopupClosingTest.java @@ -0,0 +1,81 @@ +package com.vaadin.tests.components.datefield; + +import java.io.IOException; + +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.interactions.Actions; +import org.openqa.selenium.support.ui.ExpectedCondition; +import org.openqa.selenium.support.ui.ExpectedConditions; + +import com.vaadin.testbench.elements.DateFieldElement; +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class DateFieldPopupClosingTest extends MultiBrowserTest { + + @Test + public void testDateFieldPopupClosingLongClick() + throws InterruptedException, IOException { + openTestURL(); + + fastClickDateDatePickerButton(); + + assertThatPopupIsVisible(); + + longClickDateDatePickerButton(); + + assertThatPopupIsInvisible(); + } + + private void assertThatPopupIsVisible() { + waitUntil(ExpectedConditions.visibilityOfElementLocated(By + .className("v-datefield-popup"))); + } + + private void assertThatPopupIsInvisible() { + // ExpectedConditions.invisibilityOfElementLocated doesn't work + // with PhantomJS when running with a hub: + // https://code.google.com/p/selenium/issues/detail?id=5000 + // so we need to make our own. + + waitUntil(new ExpectedCondition() { + @Override + public Boolean apply(WebDriver input) { + try { + return !(findElement(By.className("v-datefield-popup")) + .isDisplayed()); + } catch (Exception e) { + return true; + } + } + + @Override + public String toString() { + // Timed out after 10 seconds waiting for ... + return "popup to not be visible"; + } + }); + } + + private void longClickDateDatePickerButton() { + WebElement button = getToggleButton(); + + new Actions(getDriver()).clickAndHold(button).perform(); + assertThatPopupIsInvisible(); + + new Actions(getDriver()).release(button).perform(); + } + + private WebElement getToggleButton() { + DateFieldElement dateField = $(DateFieldElement.class).first(); + + return dateField.findElement(By.tagName("button")); + } + + private void fastClickDateDatePickerButton() { + getToggleButton().click(); + } + +} \ No newline at end of file -- cgit v1.2.3 From db14140a6acf6db78ca86d77d8bcbf1db0d36600 Mon Sep 17 00:00:00 2001 From: Sergey Budkin Date: Thu, 13 Nov 2014 16:47:15 +0200 Subject: Accordion causes SEVERE error in browser console (#15182) Refactored to remove invisible widgets from DOM. Change-Id: I94d0ba37f4a75d71df88fbb0b1767ae60f39432d --- client/src/com/vaadin/client/ui/VAccordion.java | 38 ++++++++++++---------- .../client/ui/accordion/AccordionConnector.java | 5 +-- .../accordion/AccordionRemoveTabTest.java | 13 ++++++++ 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/client/src/com/vaadin/client/ui/VAccordion.java b/client/src/com/vaadin/client/ui/VAccordion.java index 3e89958a98..ff77a8cb91 100644 --- a/client/src/com/vaadin/client/ui/VAccordion.java +++ b/client/src/com/vaadin/client/ui/VAccordion.java @@ -159,6 +159,8 @@ public class VAccordion extends VTabsheetBase { */ public class StackItem extends ComplexPanel implements ClickHandler { + private Widget widget; + public void setHeight(int height) { if (height == -1) { super.setHeight(""); @@ -171,10 +173,7 @@ public class VAccordion extends VTabsheetBase { } public Widget getComponent() { - if (getWidgetCount() < 2) { - return null; - } - return getWidget(1); + return widget; } @Override @@ -268,24 +267,26 @@ public class VAccordion extends VTabsheetBase { } public Widget getChildWidget() { - if (getWidgetCount() > 1) { - return getWidget(1); - } else { - return null; - } + return widget; } public void replaceWidget(Widget newWidget) { - if (getWidgetCount() > 1) { - Widget oldWidget = getWidget(1); - remove(oldWidget); - widgets.remove(oldWidget); + if (widget != null) { + widgets.remove(widget); + if (open) { + remove(widget); + } } - add(newWidget, content); + widget = newWidget; widgets.add(newWidget); + if (open) { + add(widget, content); + } + } public void open() { + add(widget, content); open = true; content.getStyle().setTop(getCaptionHeight(), Unit.PX); content.getStyle().setLeft(0, Unit.PX); @@ -298,6 +299,9 @@ public class VAccordion extends VTabsheetBase { } public void close() { + if (widget != null) { + remove(widget); + } content.getStyle().setVisibility(Visibility.HIDDEN); content.getStyle().setTop(-100000, Unit.PX); content.getStyle().setLeft(-100000, Unit.PX); @@ -322,10 +326,10 @@ public class VAccordion extends VTabsheetBase { * new content */ public void setContent(Widget newWidget) { - if (getChildWidget() == null) { - add(newWidget, content); + if (widget == null) { + widget = newWidget; widgets.add(newWidget); - } else if (getChildWidget() != newWidget) { + } else if (widget != newWidget) { replaceWidget(newWidget); } if (isOpen() && isDynamicHeight()) { diff --git a/client/src/com/vaadin/client/ui/accordion/AccordionConnector.java b/client/src/com/vaadin/client/ui/accordion/AccordionConnector.java index c0caded759..72aa2dbdfd 100644 --- a/client/src/com/vaadin/client/ui/accordion/AccordionConnector.java +++ b/client/src/com/vaadin/client/ui/accordion/AccordionConnector.java @@ -50,12 +50,13 @@ public class AccordionConnector extends TabsheetBaseConnector implements StackItem selectedItem = getWidget().getStackItem( getWidget().selectedItemIndex); - getWidget().open(getWidget().selectedItemIndex); - ComponentConnector contentConnector = getChildComponents().get(0); if (contentConnector != null) { selectedItem.setContent(contentConnector.getWidget()); } + + getWidget().open(getWidget().selectedItemIndex); + } else if (getWidget().getOpenStackItem() != null) { getWidget().close(getWidget().getOpenStackItem()); } diff --git a/uitest/src/com/vaadin/tests/components/accordion/AccordionRemoveTabTest.java b/uitest/src/com/vaadin/tests/components/accordion/AccordionRemoveTabTest.java index f5651e0ada..1047c070c8 100644 --- a/uitest/src/com/vaadin/tests/components/accordion/AccordionRemoveTabTest.java +++ b/uitest/src/com/vaadin/tests/components/accordion/AccordionRemoveTabTest.java @@ -44,6 +44,19 @@ public class AccordionRemoveTabTest extends MultiBrowserTest { checkFirstItemHeight("On third tab"); } + @Test + public void testConsoleErrorOnSwitch() { + setDebug(true); + openTestURL(); + WebElement firstItem = driver.findElement(By + .className("v-accordion-item-first")); + WebElement caption = firstItem.findElement(By + .className("v-accordion-item-caption")); + caption.click(); + Assert.assertEquals("Errors present in console", 0, + findElements(By.className("SEVERE")).size()); + } + private void checkFirstItemHeight(String text) { WebElement firstItem = driver.findElement(By .className("v-accordion-item-first")); -- cgit v1.2.3 From 52236519d9d3ed0cd7de5ed52c6aa19c514e15d6 Mon Sep 17 00:00:00 2001 From: Sara Seppola Date: Thu, 6 Nov 2014 15:46:17 +0200 Subject: TextField's input prompt is now disabled properly (#15144) Change-Id: I9b5d07ec2d8df78c3f120c2a891cc548da787f38 --- .../client/ui/textfield/TextFieldConnector.java | 15 +- .../textfield/AlternatingTextFields.java | 106 +++++++++++ .../textfield/AlternatingTextFieldsTest.java | 212 +++++++++++++++++++++ 3 files changed, 329 insertions(+), 4 deletions(-) create mode 100644 uitest/src/com/vaadin/tests/components/textfield/AlternatingTextFields.java create mode 100644 uitest/src/com/vaadin/tests/components/textfield/AlternatingTextFieldsTest.java diff --git a/client/src/com/vaadin/client/ui/textfield/TextFieldConnector.java b/client/src/com/vaadin/client/ui/textfield/TextFieldConnector.java index 1a4b64b0a6..cba827bcef 100644 --- a/client/src/com/vaadin/client/ui/textfield/TextFieldConnector.java +++ b/client/src/com/vaadin/client/ui/textfield/TextFieldConnector.java @@ -87,10 +87,17 @@ public class TextFieldConnector extends AbstractFieldConnector implements * change in the queue (in which case we count more on the server side * value). */ - if (!(uidl - .getBooleanAttribute(TextFieldConstants.ATTR_NO_VALUE_CHANGE_BETWEEN_PAINTS) - && getWidget().valueBeforeEdit != null && text - .equals(getWidget().valueBeforeEdit))) { + + boolean valueChanged = !uidl + .getBooleanAttribute(TextFieldConstants.ATTR_NO_VALUE_CHANGE_BETWEEN_PAINTS); + // null check is not enough since the value is sometimes null but + // sometimes empty. Fix for #15144 + boolean valueBeforeEditEmpty = getWidget().valueBeforeEdit == null + || getWidget().valueBeforeEdit.isEmpty(); + boolean textDoesNotEqualOldValue = !text + .equals(getWidget().valueBeforeEdit); + + if (valueChanged || valueBeforeEditEmpty || textDoesNotEqualOldValue) { getWidget().updateFieldContent(text); } diff --git a/uitest/src/com/vaadin/tests/components/textfield/AlternatingTextFields.java b/uitest/src/com/vaadin/tests/components/textfield/AlternatingTextFields.java new file mode 100644 index 0000000000..3eda11d999 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/textfield/AlternatingTextFields.java @@ -0,0 +1,106 @@ +/* + * 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.textfield; + +import com.vaadin.event.FieldEvents.TextChangeEvent; +import com.vaadin.event.FieldEvents.TextChangeListener; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.TextField; +import com.vaadin.ui.VerticalLayout; + +/** + * When two TextFields repeatedly disable each other, ensure that also their + * input prompts are removed + * + * @since + * @author Vaadin Ltd + */ +public class AlternatingTextFields extends AbstractTestUI { + + public static final String FIRST_TEXTFIELD_INPUT_PROMPT = "Enter first data here"; + public static final String SECOND_TEXTFIELD_INPUT_PROMPT = "Enter second data here"; + + @Override + protected void setup(final VaadinRequest request) { + + VerticalLayout layout = new VerticalLayout(); + layout.setMargin(true); + layout.setSpacing(true); + + final TextField firstTextField = createTextField("First", + FIRST_TEXTFIELD_INPUT_PROMPT); + + final TextField secondTextField = createTextField("Second", + SECOND_TEXTFIELD_INPUT_PROMPT); + + addTextChangeListener(firstTextField, secondTextField); + addTextChangeListener(secondTextField, firstTextField); + + layout.addComponent(firstTextField); + layout.addComponent(secondTextField); + + addComponent(layout); + } + + private static TextField createTextField(String number, String inputPrompt) { + + String caption = " TextField with TextChangeListener"; + + TextField textField = new TextField(number + caption); + textField.setImmediate(true); + textField.setInputPrompt(inputPrompt); + + return textField; + } + + private void addTextChangeListener(TextField currentTextField, + final TextField otherTextField) { + + final String otherDefaultPrompt = otherTextField.getInputPrompt(); + currentTextField.addTextChangeListener(new TextChangeListener() { + + @Override + public void textChange(TextChangeEvent event) { + + String currentText = event.getText(); + + if (currentText.isEmpty() || currentText == null) { + // change other to default + + otherTextField.setInputPrompt(otherDefaultPrompt); + otherTextField.setEnabled(true); + } else { + // change other to empty + + otherTextField.setInputPrompt(null); + otherTextField.setEnabled(false); + } + + } + }); + } + + @Override + protected String getTestDescription() { + return "When two TextFields repeatedly disable each other, ensure that also their input prompts are removed"; + } + + @Override + protected Integer getTicketNumber() { + return 15144; + } +} diff --git a/uitest/src/com/vaadin/tests/components/textfield/AlternatingTextFieldsTest.java b/uitest/src/com/vaadin/tests/components/textfield/AlternatingTextFieldsTest.java new file mode 100644 index 0000000000..a7ee3fede2 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/textfield/AlternatingTextFieldsTest.java @@ -0,0 +1,212 @@ +/* + * 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.textfield; + +import static org.junit.Assert.assertEquals; + +import java.util.List; + +import org.junit.Test; +import org.openqa.selenium.Keys; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.support.ui.ExpectedCondition; + +import com.vaadin.testbench.elements.TextFieldElement; +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class AlternatingTextFieldsTest extends MultiBrowserTest { + + private String RANDOM_INPUT = "Some input here"; + + private List textfields; + + @Test + public void testInputPrompt() { + openTestURL(); + + /* + * Starting positions + */ + + createTextFields(); + + // test that both input prompts exist in the beginning + ensureTextFieldHasInputPrompt(0); + ensureTextFieldHasInputPrompt(1); + + /* + * Write on and empty the first TextField + */ + + // select first input prompt + ensureSelectionClearsPrompt(0); + + // write on the first TextField + ensureWritingDisablesOther(0); + + // empty the text on the first TextField + ensureEmptyingAddsPromptAndEnablesOther(0); + + /* + * Write on and empty the second TextField + */ + + // now select the second input prompt + ensureSelectionClearsPrompt(1); + + // write on the second TextField + ensureWritingDisablesOther(1); + + // empty the text on the second TextField + ensureEmptyingAddsPromptAndEnablesOther(1); + + } + + private void ensureEmptyingAddsPromptAndEnablesOther(int index) { + // remove the text from the TextField + emptyTextField(index); + + // ensure that the TextField really is empty + ensureTextFieldEmpty(index); + + // ensure that the other TextField has again been enabled and has an + // input prompt + if (index == 0) { + ensureTextFieldIsEnabledAndHasInputPrompt(1); + } else { + ensureTextFieldIsEnabledAndHasInputPrompt(0); + } + } + + private void ensureWritingDisablesOther(int index) { + // write some text to the TextField + writeOnTextField(index); + + // ensure that the other TextField is disabled and has no input prompt + if (index == 0) { + ensureTextFieldDisabledAndEmpty(1); + } else { + ensureTextFieldDisabledAndEmpty(0); + } + } + + private void ensureSelectionClearsPrompt(int index) { + // select the TextField + textfields.get(index).click(); + + // check that the the prompt was removed + ensureTextFieldEmpty(index); + } + + /** + * Check that the TextField has no input prompt + * + * @since + * @param index + * The TextField to be inspected + */ + private void ensureTextFieldEmpty(int index) { + + assertEquals("TextField " + index + " was not empty,", "", textfields + .get(index).getValue()); + } + + /** + * Check that the TextField has been enabled and has correct input prompt + * + * @since + * @param index + * the TextField to be inspected + */ + private void ensureTextFieldIsEnabledAndHasInputPrompt(final int index) { + + waitUntil(new ExpectedCondition() { + + @Override + public Boolean apply(WebDriver input) { + return textfields.get(index).isEnabled(); + } + + @Override + public String toString() { + // Timed out after 10 seconds waiting for ... + return "TextField " + index + " to be enabled"; + } + }); + + ensureTextFieldHasInputPrompt(index); + } + + /** + * Check that the TextField has the correct input prompt + * + * @since + * @param index + * The TextField to be inspected + */ + private void ensureTextFieldHasInputPrompt(final int index) { + + if (index == 0) { + assertEquals("Incorrect or missing prompt,", + AlternatingTextFields.FIRST_TEXTFIELD_INPUT_PROMPT, + textfields.get(index).getValue()); + } else { + assertEquals("Incorrect or missing prompt,", + AlternatingTextFields.SECOND_TEXTFIELD_INPUT_PROMPT, + textfields.get(index).getValue()); + } + } + + /** + * Check that the TextField has been disabled and has no input prompt + * + * @since + * @param index + * The TextField to be inspected + */ + private void ensureTextFieldDisabledAndEmpty(final int index) { + + waitUntil(new ExpectedCondition() { + + @Override + public Boolean apply(WebDriver input) { + return !textfields.get(index).isEnabled(); + } + + @Override + public String toString() { + // Timed out after 10 seconds waiting for ... + return "TextField " + index + " to be disabled"; + } + }); + + ensureTextFieldEmpty(index); + } + + private void createTextFields() { + textfields = $(TextFieldElement.class).all(); + } + + private void writeOnTextField(int index) { + textfields.get(index).sendKeys(RANDOM_INPUT); + } + + private void emptyTextField(int index) { + for (int i = 0; i < 15; i++) { + textfields.get(index).sendKeys(Keys.BACK_SPACE); + } + } +} -- cgit v1.2.3 From 67b20a12d6016654663eb2726c2a095a6935593a Mon Sep 17 00:00:00 2001 From: Sauli Tähkäpää Date: Wed, 10 Dec 2014 12:38:45 +0200 Subject: Set locale explicitly in DateRangeWithSql. (#15342) Change-Id: I5f73ac5e8bc6be93fa0f9b9089b1584fb35f4a76 --- .../com/vaadin/tests/components/datefield/DateRangeWithSqlDate.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/uitest/src/com/vaadin/tests/components/datefield/DateRangeWithSqlDate.java b/uitest/src/com/vaadin/tests/components/datefield/DateRangeWithSqlDate.java index 74d3e85892..d3bc4267ec 100644 --- a/uitest/src/com/vaadin/tests/components/datefield/DateRangeWithSqlDate.java +++ b/uitest/src/com/vaadin/tests/components/datefield/DateRangeWithSqlDate.java @@ -20,6 +20,8 @@ import com.vaadin.tests.components.AbstractTestUI; import com.vaadin.ui.DateField; import com.vaadin.ui.InlineDateField; +import java.util.Locale; + public class DateRangeWithSqlDate extends AbstractTestUI { // 2014-12-01 @@ -33,6 +35,7 @@ public class DateRangeWithSqlDate extends AbstractTestUI { @Override protected void setup(VaadinRequest request) { DateField df = new InlineDateField(); + df.setLocale(Locale.US); df.setRangeStart(startDate); df.setRangeEnd(endDate); -- cgit v1.2.3 From ab9d395d3a380217d51b68a338382854bd6194e0 Mon Sep 17 00:00:00 2001 From: Fabian Lange Date: Tue, 25 Nov 2014 10:26:18 +0100 Subject: always retry a status code 0 uidl request (#15254) A status code of 0 indicates that the request has not been handled by the server because the connection was closed without sending response headers. This can happen on the browser side when the browser window is closed, or it can happen on network equipment side under adverse quality of link conditions. Therefore vaadin should retry such a request once before popping up the disconnect message. Change-Id: I9524c97a3de8e4ac9499394a2a795b0441a36ead --- .../com/vaadin/client/ApplicationConnection.java | 62 +++++++++++++--------- 1 file changed, 38 insertions(+), 24 deletions(-) diff --git a/client/src/com/vaadin/client/ApplicationConnection.java b/client/src/com/vaadin/client/ApplicationConnection.java index d7c1c54a2d..f683181857 100644 --- a/client/src/com/vaadin/client/ApplicationConnection.java +++ b/client/src/com/vaadin/client/ApplicationConnection.java @@ -66,6 +66,7 @@ import com.google.gwt.user.client.Window.ClosingHandler; import com.google.gwt.user.client.ui.HasWidgets; import com.google.gwt.user.client.ui.Widget; import com.vaadin.client.ApplicationConfiguration.ErrorMessage; +import com.vaadin.client.ApplicationConnection.ApplicationStoppedEvent; import com.vaadin.client.ResourceLoader.ResourceLoadEvent; import com.vaadin.client.ResourceLoader.ResourceLoadListener; import com.vaadin.client.communication.HasJavaScriptConnectorHelper; @@ -201,14 +202,6 @@ public class ApplicationConnection implements HasHandlers { private boolean hasActiveRequest = false; - /** - * Some browsers cancel pending XHR requests when a request that might - * navigate away from the page starts (indicated by a beforeunload event). - * In that case, we should just send the request again without displaying - * any error. - */ - private boolean retryCanceledActiveRequest = false; - /** * Webkit will ignore outgoing requests while waiting for a response to a * navigation event (indicated by a beforeunload event). When this happens, @@ -547,14 +540,6 @@ public class ApplicationConnection implements HasHandlers { Window.addWindowClosingHandler(new ClosingHandler() { @Override public void onWindowClosing(ClosingEvent event) { - /* - * Set some flags to avoid potential problems with XHR requests, - * see javadocs of the flags for details - */ - if (hasActiveRequest()) { - retryCanceledActiveRequest = true; - } - webkitMaybeIgnoringRequests = true; } }); @@ -858,6 +843,22 @@ public class ApplicationConnection implements HasHandlers { * The contents of the request to send */ protected void doUidlRequest(final String uri, final JSONObject payload) { + doUidlRequest(uri, payload, true); + } + + /** + * Sends an asynchronous or synchronous UIDL request to the server using the + * given URI. + * + * @param uri + * The URI to use for the request. May includes GET parameters + * @param payload + * The contents of the request to send + * @param retry + * true when a status code 0 should be retried + */ + protected void doUidlRequest(final String uri, final JSONObject payload, + final boolean retry) { RequestCallback requestCallback = new RequestCallback() { @Override public void onError(Request request, Throwable exception) { @@ -887,15 +888,29 @@ public class ApplicationConnection implements HasHandlers { switch (statusCode) { case 0: - if (retryCanceledActiveRequest) { + if (retry) { /* - * Request was most likely canceled because the browser - * is maybe navigating away from the page. Just send the - * request again without displaying any error in case - * the navigation isn't carried through. + * There are 2 situations where the error can pop up: + * + * 1) Request was most likely canceled because the + * browser is maybe navigating away from the page. Just + * send the request again without displaying any error + * in case the navigation isn't carried through. + * + * 2) The browser failed to establish a network + * connection. This was observed with keep-alive + * requests, and under wi-fi roaming conditions. + * + * Status code 0 does indicate that there was no server + * side processing, so we can retry the request. */ - retryCanceledActiveRequest = false; - doUidlRequest(uri, payload); + VConsole.log("Status code 0, retrying"); + (new Timer() { + @Override + public void run() { + doUidlRequest(uri, payload, false); + } + }).schedule(100); } else { handleCommunicationError( "Invalid status code 0 (server down?)", @@ -1269,7 +1284,6 @@ public class ApplicationConnection implements HasHandlers { // so setting it after used to work but not with the #8505 changes. hasActiveRequest = false; - retryCanceledActiveRequest = false; webkitMaybeIgnoringRequests = false; if (isApplicationRunning()) { -- cgit v1.2.3 From da91036c92d8088d02471bb939b21eb228638d8f Mon Sep 17 00:00:00 2001 From: Denis Anisimov Date: Sun, 14 Sep 2014 12:49:23 +0300 Subject: Set explicit calculated size for drag image element (#14617). Change-Id: I6bfcd0a5ad78bd79aad2fa2357a7e33a00b3c33d --- client/src/com/vaadin/client/ui/dd/VDragEvent.java | 5 ++ .../DragAndDropRelativeWidth.java | 58 ++++++++++++++++++++++ .../DragAndDropRelativeWidthTest.java | 57 +++++++++++++++++++++ .../draganddropwrapper/DragStartModesTest.java | 2 +- 4 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 uitest/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropRelativeWidth.java create mode 100644 uitest/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropRelativeWidthTest.java diff --git a/client/src/com/vaadin/client/ui/dd/VDragEvent.java b/client/src/com/vaadin/client/ui/dd/VDragEvent.java index 6291a38e42..45f89bdb87 100644 --- a/client/src/com/vaadin/client/ui/dd/VDragEvent.java +++ b/client/src/com/vaadin/client/ui/dd/VDragEvent.java @@ -244,6 +244,11 @@ public class VDragEvent { public void createDragImage(com.google.gwt.user.client.Element element, boolean alignImageToEvent) { Element cloneNode = (Element) element.cloneNode(true); + + // Set size explicitly for cloned node to avoid stretching #14617. + cloneNode.getStyle().setWidth(element.getOffsetWidth(), Unit.PX); + cloneNode.getStyle().setHeight(element.getOffsetHeight(), Unit.PX); + syncContent(element, cloneNode); if (BrowserInfo.get().isIE()) { if (cloneNode.getTagName().toLowerCase().equals("tr")) { diff --git a/uitest/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropRelativeWidth.java b/uitest/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropRelativeWidth.java new file mode 100644 index 0000000000..90ec704a8c --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropRelativeWidth.java @@ -0,0 +1,58 @@ +/* + * 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.draganddropwrapper; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.CssLayout; +import com.vaadin.ui.DragAndDropWrapper; +import com.vaadin.ui.DragAndDropWrapper.DragStartMode; +import com.vaadin.ui.Label; + +/** + * Test UI for DnD image element size + * + * @author Vaadin Ltd + */ +public class DragAndDropRelativeWidth extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + CssLayout layout = new CssLayout(); + layout.setWidth(300, Unit.PIXELS); + + Label label = new Label("drag source"); + label.addStyleName("drag-source"); + label.setWidth(100, Unit.PERCENTAGE); + DragAndDropWrapper wrapper = new DragAndDropWrapper(label); + wrapper.setWidth(100, Unit.PERCENTAGE); + wrapper.setDragStartMode(DragStartMode.COMPONENT); + + layout.addComponent(wrapper); + addComponent(layout); + } + + @Override + protected String getTestDescription() { + return "Set explicit size for drag image element using calclulated size from the source"; + } + + @Override + protected Integer getTicketNumber() { + return 14617; + } + +} diff --git a/uitest/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropRelativeWidthTest.java b/uitest/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropRelativeWidthTest.java new file mode 100644 index 0000000000..9cb7a63046 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropRelativeWidthTest.java @@ -0,0 +1,57 @@ +/* + * 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.draganddropwrapper; + +import org.junit.Assert; +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.Dimension; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.interactions.Actions; + +import com.vaadin.tests.tb3.MultiBrowserTest; + +/** + * Test to check size of drag image element. + * + * @author Vaadin Ltd + */ +public class DragAndDropRelativeWidthTest extends MultiBrowserTest { + + @Test + public void testDragImageElementSize() { + openTestURL(); + + WebElement label = getDriver().findElement(By.className("drag-source")); + Dimension size = label.getSize(); + int height = size.getHeight(); + int width = size.getWidth(); + Actions actions = new Actions(getDriver()); + actions.moveToElement(label); + actions.clickAndHold(); + actions.moveByOffset(100, 100); + actions.build().perform(); + + WebElement dragImage = getDriver().findElement( + By.className("v-drag-element")); + + Assert.assertEquals("Drag image element height is unexpected", height, + dragImage.getSize().getHeight()); + Assert.assertEquals("Drag image element width is unexpected", width, + dragImage.getSize().getWidth()); + } + +} diff --git a/uitest/src/com/vaadin/tests/components/draganddropwrapper/DragStartModesTest.java b/uitest/src/com/vaadin/tests/components/draganddropwrapper/DragStartModesTest.java index ba27ee293e..4e60b43ea2 100644 --- a/uitest/src/com/vaadin/tests/components/draganddropwrapper/DragStartModesTest.java +++ b/uitest/src/com/vaadin/tests/components/draganddropwrapper/DragStartModesTest.java @@ -40,7 +40,7 @@ public class DragStartModesTest extends MultiBrowserTest { new Actions(driver).moveToElement(draggable, 10, 10).clickAndHold() .moveByOffset(5, 0).perform(); new Actions(driver).moveToElement(dropTarget, 12, 10).perform(); - compareScreen("dragMode" + dragMode); + compareScreen("dragImageMode" + dragMode); new Actions(driver).release().perform(); } -- cgit v1.2.3 From 60f0bdaa6ef00f14664ead0d0b4aefb18343c8a7 Mon Sep 17 00:00:00 2001 From: Denis Anisimov Date: Fri, 31 Oct 2014 20:33:39 +0200 Subject: Horizontal split panel height shouldn't ignore second component (#15149) Change-Id: Ie925757167b6d8521707b6d5515157eca85f9dba --- .../com/vaadin/client/ui/VAbstractSplitPanel.java | 194 +++++++++++---------- .../splitpanel/HorizontalSplitPanelHeight.java | 87 +++++++++ .../splitpanel/HorizontalSplitPanelHeightTest.java | 71 ++++++++ 3 files changed, 261 insertions(+), 91 deletions(-) create mode 100644 uitest/src/com/vaadin/tests/components/splitpanel/HorizontalSplitPanelHeight.java create mode 100644 uitest/src/com/vaadin/tests/components/splitpanel/HorizontalSplitPanelHeightTest.java diff --git a/client/src/com/vaadin/client/ui/VAbstractSplitPanel.java b/client/src/com/vaadin/client/ui/VAbstractSplitPanel.java index 6ee88d51dd..9d32355b70 100644 --- a/client/src/com/vaadin/client/ui/VAbstractSplitPanel.java +++ b/client/src/com/vaadin/client/ui/VAbstractSplitPanel.java @@ -199,6 +199,7 @@ public class VAbstractSplitPanel extends ComplexPanel { private void setOrientation(Orientation orientation) { this.orientation = orientation; + if (orientation == Orientation.HORIZONTAL) { splitter.getStyle().setHeight(100, Unit.PCT); splitter.getStyle().setTop(0, Unit.PX); @@ -393,107 +394,118 @@ public class VAbstractSplitPanel extends ComplexPanel { return; } - int wholeSize; - int pixelPosition; - switch (orientation) { case HORIZONTAL: - wholeSize = DOM.getElementPropertyInt(wrapper, "clientWidth"); - pixelPosition = DOM.getElementPropertyInt(splitter, "offsetLeft"); - - // reposition splitter in case it is out of box - if ((pixelPosition > 0 && pixelPosition + getSplitterSize() > wholeSize) - || (positionReversed && pixelPosition < 0)) { - pixelPosition = wholeSize - getSplitterSize(); - if (pixelPosition < 0) { - pixelPosition = 0; - } - // Move splitter within bounds, but don't remember the new value - setSplitPosition(pixelPosition + "px", false); - return; - } + horizontalOrientationUpdateSizes(); + break; + case VERTICAL: + verticalOrientationUpdateSizes(); + break; + } + } - firstContainer.getStyle().setWidth(pixelPosition, Unit.PX); - int secondContainerWidth = (wholeSize - pixelPosition - getSplitterSize()); - if (secondContainerWidth < 0) { - secondContainerWidth = 0; - } - secondContainer.getStyle().setWidth(secondContainerWidth, Unit.PX); - secondContainer.getStyle().setLeft( - pixelPosition + getSplitterSize(), Unit.PX); - - LayoutManager layoutManager = LayoutManager.get(client); - ConnectorMap connectorMap = ConnectorMap.get(client); - if (firstChild != null) { - ComponentConnector connector = connectorMap - .getConnector(firstChild); - if (connector.isRelativeWidth()) { - layoutManager.reportWidthAssignedToRelative(connector, - pixelPosition); - } else { - layoutManager.setNeedsMeasure(connector); - } + private void verticalOrientationUpdateSizes() { + int wholeSize = DOM.getElementPropertyInt(wrapper, "clientHeight"); + int pixelPosition = DOM.getElementPropertyInt(splitter, "offsetTop"); + + // reposition splitter in case it is out of box + if ((pixelPosition > 0 && pixelPosition + getSplitterSize() > wholeSize) + || (positionReversed && pixelPosition < 0)) { + pixelPosition = wholeSize - getSplitterSize(); + if (pixelPosition < 0) { + pixelPosition = 0; } - if (secondChild != null) { - ComponentConnector connector = connectorMap - .getConnector(secondChild); - if (connector.isRelativeWidth()) { - layoutManager.reportWidthAssignedToRelative(connector, - secondContainerWidth); - } else { - layoutManager.setNeedsMeasure(connector); - } + // Move splitter within bounds, but don't remember the new value + setSplitPosition(pixelPosition + "px", false); + return; + } + + firstContainer.getStyle().setHeight(pixelPosition, Unit.PX); + int secondContainerHeight = (wholeSize - pixelPosition - getSplitterSize()); + if (secondContainerHeight < 0) { + secondContainerHeight = 0; + } + secondContainer.getStyle().setHeight(secondContainerHeight, Unit.PX); + secondContainer.getStyle().setTop(pixelPosition + getSplitterSize(), + Unit.PX); + + LayoutManager layoutManager = LayoutManager.get(client); + ConnectorMap connectorMap = ConnectorMap.get(client); + if (firstChild != null) { + ComponentConnector connector = connectorMap + .getConnector(firstChild); + if (connector.isRelativeHeight()) { + layoutManager.reportHeightAssignedToRelative(connector, + pixelPosition); + } else { + layoutManager.setNeedsMeasure(connector); } - break; - case VERTICAL: - wholeSize = DOM.getElementPropertyInt(wrapper, "clientHeight"); - pixelPosition = DOM.getElementPropertyInt(splitter, "offsetTop"); - - // reposition splitter in case it is out of box - if ((pixelPosition > 0 && pixelPosition + getSplitterSize() > wholeSize) - || (positionReversed && pixelPosition < 0)) { - pixelPosition = wholeSize - getSplitterSize(); - if (pixelPosition < 0) { - pixelPosition = 0; - } - // Move splitter within bounds, but don't remember the new value - setSplitPosition(pixelPosition + "px", false); - return; + } + if (secondChild != null) { + ComponentConnector connector = connectorMap + .getConnector(secondChild); + if (connector.isRelativeHeight()) { + layoutManager.reportHeightAssignedToRelative(connector, + secondContainerHeight); + } else { + layoutManager.setNeedsMeasure(connector); } + } + } + + private void horizontalOrientationUpdateSizes() { + int wholeSize = DOM.getElementPropertyInt(wrapper, "clientWidth"); + int pixelPosition = DOM.getElementPropertyInt(splitter, "offsetLeft"); - firstContainer.getStyle().setHeight(pixelPosition, Unit.PX); - int secondContainerHeight = (wholeSize - pixelPosition - getSplitterSize()); - if (secondContainerHeight < 0) { - secondContainerHeight = 0; + // reposition splitter in case it is out of box + if ((pixelPosition > 0 && pixelPosition + getSplitterSize() > wholeSize) + || (positionReversed && pixelPosition < 0)) { + pixelPosition = wholeSize - getSplitterSize(); + if (pixelPosition < 0) { + pixelPosition = 0; } - secondContainer.getStyle() - .setHeight(secondContainerHeight, Unit.PX); - secondContainer.getStyle().setTop( - pixelPosition + getSplitterSize(), Unit.PX); - - layoutManager = LayoutManager.get(client); - connectorMap = ConnectorMap.get(client); - if (firstChild != null) { - ComponentConnector connector = connectorMap - .getConnector(firstChild); - if (connector.isRelativeHeight()) { - layoutManager.reportHeightAssignedToRelative(connector, - pixelPosition); - } else { - layoutManager.setNeedsMeasure(connector); - } + // Move splitter within bounds, but don't remember the new value + setSplitPosition(pixelPosition + "px", false); + return; + } + + firstContainer.getStyle().setWidth(pixelPosition, Unit.PX); + int secondContainerWidth = (wholeSize - pixelPosition - getSplitterSize()); + if (secondContainerWidth < 0) { + secondContainerWidth = 0; + } + secondContainer.getStyle().setWidth(secondContainerWidth, Unit.PX); + secondContainer.getStyle().setLeft(pixelPosition + getSplitterSize(), + Unit.PX); + + LayoutManager layoutManager = LayoutManager.get(client); + ConnectorMap connectorMap = ConnectorMap.get(client); + if (firstChild != null) { + ComponentConnector connector = connectorMap + .getConnector(firstChild); + if (connector.isRelativeWidth()) { + layoutManager.reportWidthAssignedToRelative(connector, + pixelPosition); + } else { + layoutManager.setNeedsMeasure(connector); } - if (secondChild != null) { - ComponentConnector connector = connectorMap - .getConnector(secondChild); - if (connector.isRelativeHeight()) { - layoutManager.reportHeightAssignedToRelative(connector, - secondContainerHeight); - } else { - layoutManager.setNeedsMeasure(connector); - } + } + if (secondChild != null) { + ComponentConnector connector = connectorMap + .getConnector(secondChild); + if (connector.isRelativeWidth()) { + layoutManager.reportWidthAssignedToRelative(connector, + secondContainerWidth); + } else { + layoutManager.setNeedsMeasure(connector); } - break; + } + + // previous layout pass may have changed the position already, needs to + // be reset before calculating which positioning should be used + secondContainer.getStyle().setPosition(Position.ABSOLUTE); + if (getOffsetHeight() == 0) { + secondContainer.getStyle().setPosition(Position.RELATIVE); } } diff --git a/uitest/src/com/vaadin/tests/components/splitpanel/HorizontalSplitPanelHeight.java b/uitest/src/com/vaadin/tests/components/splitpanel/HorizontalSplitPanelHeight.java new file mode 100644 index 0000000000..4da5430cb1 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/splitpanel/HorizontalSplitPanelHeight.java @@ -0,0 +1,87 @@ +/* + * 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.splitpanel; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.shared.ui.MarginInfo; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Button; +import com.vaadin.ui.HorizontalSplitPanel; +import com.vaadin.ui.Label; +import com.vaadin.ui.VerticalSplitPanel; + +/** + * Test UI for horizontal split panel height. + * + * @author Vaadin Ltd + */ +public class HorizontalSplitPanelHeight extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + getLayout().setMargin(new MarginInfo(true, false, false, false)); + + HorizontalSplitPanel panel = new HorizontalSplitPanel(); + panel.setCaption("Horizontal 1 - no first component, label as second component"); + panel.setId("Horizontal 1"); + Label label = new Label("Label"); + label.addStyleName("target"); + panel.setSecondComponent(label); + + addComponent(panel); + + panel = new HorizontalSplitPanel(); + panel.setCaption("Horizontal 2 - button as first component, label as second component"); + panel.setId("Horizontal 2"); + label = new Label("Label"); + label.addStyleName("target"); + panel.setSecondComponent(label); + panel.setFirstComponent(new Button("button")); + + addComponent(panel); + + panel = new HorizontalSplitPanel(); + panel.setCaption("Horizontal 3 - fixed height, no first component, label as second component"); + panel.setId("Horizontal 3"); + label = new Label("Label"); + label.addStyleName("target"); + panel.setSecondComponent(label); + panel.setHeight(30, Unit.PIXELS); + + addComponent(panel); + + VerticalSplitPanel vPanel = new VerticalSplitPanel(); + vPanel.setCaption("Vertical 1 - no first component, label as second component"); + vPanel.setId("Vertical 1"); + vPanel.setHeight(100, Unit.PIXELS); + Label vLabel = new Label("Label"); + vLabel.addStyleName("target"); + vPanel.setSecondComponent(vLabel); + + addComponent(vPanel); + + } + + @Override + protected Integer getTicketNumber() { + return 15149; + } + + @Override + public String getTestDescription() { + return "Height of split panel should be greater than height of second component."; + } +} diff --git a/uitest/src/com/vaadin/tests/components/splitpanel/HorizontalSplitPanelHeightTest.java b/uitest/src/com/vaadin/tests/components/splitpanel/HorizontalSplitPanelHeightTest.java new file mode 100644 index 0000000000..0ceca66dd3 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/splitpanel/HorizontalSplitPanelHeightTest.java @@ -0,0 +1,71 @@ +/* + * 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.splitpanel; + +import org.junit.Assert; +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; + +import com.vaadin.tests.tb3.MultiBrowserTest; + +/** + * Test for horizontal split panel height in case when only second component is + * set. + * + * @author Vaadin Ltd + */ +public class HorizontalSplitPanelHeightTest extends MultiBrowserTest { + + @Override + public void setup() throws Exception { + super.setup(); + + openTestURL(); + } + + @Test + public void testHorizontalWithoutFirstComponent() { + testSplitPanel("Horizontal 1"); + } + + @Test + public void testHorizontalWithFirstComponent() { + testSplitPanel("Horizontal 2"); + } + + @Test + public void testHorizontalWithFixedHeight() { + testSplitPanel("Horizontal 3"); + } + + @Test + public void testVerticalWithoutFirstComponent() { + testSplitPanel("Vertical 1"); + } + + private void testSplitPanel(String id) { + WebElement splitPanel = findElement(By.id(id)); + WebElement label = splitPanel.findElement(By.className("target")); + Assert.assertTrue(id + ": split panel height (" + + splitPanel.getSize().getHeight() + ") is less than " + + "height of second component (" + label.getSize().getHeight() + + ")", splitPanel.getSize().getHeight() >= label.getSize() + .getHeight()); + Assert.assertEquals("Label text in the second panel is not visible", + "Label", label.getText()); + } +} -- cgit v1.2.3 From 9107b8d8c520bd297ed7685d7a8482d1078604e9 Mon Sep 17 00:00:00 2001 From: Teemu Pöntelin Date: Thu, 4 Dec 2014 20:09:42 +0200 Subject: Fix CustomLayout child rendering when template is missing (#8696) Change-Id: I8ce4fbc566f030bf22c555f68b97beb781b19805 --- .../ui/customlayout/CustomLayoutConnector.java | 23 ++++++++--- .../customlayout/CustomLayoutWithoutTemplate.java | 44 ++++++++++++++++++++++ .../CustomLayoutWithoutTemplateTest.java | 43 +++++++++++++++++++++ 3 files changed, 104 insertions(+), 6 deletions(-) create mode 100644 uitest/src/com/vaadin/tests/components/customlayout/CustomLayoutWithoutTemplate.java create mode 100644 uitest/src/com/vaadin/tests/components/customlayout/CustomLayoutWithoutTemplateTest.java diff --git a/client/src/com/vaadin/client/ui/customlayout/CustomLayoutConnector.java b/client/src/com/vaadin/client/ui/customlayout/CustomLayoutConnector.java index a37ce9af38..80979587b9 100644 --- a/client/src/com/vaadin/client/ui/customlayout/CustomLayoutConnector.java +++ b/client/src/com/vaadin/client/ui/customlayout/CustomLayoutConnector.java @@ -34,6 +34,8 @@ import com.vaadin.ui.CustomLayout; public class CustomLayoutConnector extends AbstractLayoutConnector implements SimpleManagedLayout, Paintable { + private boolean templateUpdated; + @Override public CustomLayoutState getState() { return (CustomLayoutState) super.getState(); @@ -62,7 +64,7 @@ public class CustomLayoutConnector extends AbstractLayoutConnector implements } private void updateHtmlTemplate() { - if (getWidget().hasTemplate()) { + if (templateUpdated) { // We (currently) only do this once. You can't change the template // later on. return; @@ -76,14 +78,23 @@ public class CustomLayoutConnector extends AbstractLayoutConnector implements templateContents = getConnection().getResource( "layouts/" + templateName + ".html"); if (templateContents == null) { - templateContents = "Layout file layouts/" - + templateName - + ".html is missing. Components will be drawn for debug purposes."; + // Template missing -> show debug notice and render components + // in order. + getWidget() + .getElement() + .setInnerHTML( + "Layout file layouts/" + + templateName + + ".html is missing. Components will be drawn for debug purposes."); } } - getWidget().initializeHTML(templateContents, - getConnection().getThemeUri()); + if (templateContents != null) { + // Template ok -> initialize. + getWidget().initializeHTML(templateContents, + getConnection().getThemeUri()); + } + templateUpdated = true; } @Override diff --git a/uitest/src/com/vaadin/tests/components/customlayout/CustomLayoutWithoutTemplate.java b/uitest/src/com/vaadin/tests/components/customlayout/CustomLayoutWithoutTemplate.java new file mode 100644 index 0000000000..54949a053d --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/customlayout/CustomLayoutWithoutTemplate.java @@ -0,0 +1,44 @@ +/* + * 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.customlayout; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Button; +import com.vaadin.ui.CustomLayout; +import com.vaadin.ui.Label; + +public class CustomLayoutWithoutTemplate extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + CustomLayout cl = new CustomLayout("missing-layout-file.html"); + cl.addComponent(new Label("This Label should be visible."), "foo"); + cl.addComponent(new Button("And this Button too."), "bar"); + + addComponent(cl); + } + + @Override + protected String getTestDescription() { + return "Verify that CustomLayout renders child components even if the template is missing."; + } + + @Override + protected Integer getTicketNumber() { + return 8696; + } +} diff --git a/uitest/src/com/vaadin/tests/components/customlayout/CustomLayoutWithoutTemplateTest.java b/uitest/src/com/vaadin/tests/components/customlayout/CustomLayoutWithoutTemplateTest.java new file mode 100644 index 0000000000..695d9cceff --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/customlayout/CustomLayoutWithoutTemplateTest.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.customlayout; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import com.vaadin.testbench.ElementQuery; +import com.vaadin.testbench.elements.ButtonElement; +import com.vaadin.testbench.elements.CustomLayoutElement; +import com.vaadin.testbench.elements.LabelElement; +import com.vaadin.tests.tb3.SingleBrowserTest; + +public class CustomLayoutWithoutTemplateTest extends SingleBrowserTest { + + @Test + public void testChildComponents() { + openTestURL(); + + ElementQuery customLayout = $(CustomLayoutElement.class); + + // Verify the Button and Label are rendered inside the CustomLayout. + assertTrue("Button was not rendered.", + customLayout.$(ButtonElement.class).exists()); + assertTrue("Label was not rendered.", customLayout + .$(LabelElement.class).exists()); + } + +} -- cgit v1.2.3 From 22b20bc9fc4067a026cf08b5130bd20afa89d27e Mon Sep 17 00:00:00 2001 From: Artur Signell Date: Tue, 9 Dec 2014 16:58:01 +0200 Subject: Add public Field.isEmpty() and clear() (#15354) Change-Id: I6bda7ff2a66a9ad172c899d855ca868881600be4 --- server/src/com/vaadin/ui/AbstractField.java | 19 +----- server/src/com/vaadin/ui/AbstractSelect.java | 2 +- server/src/com/vaadin/ui/AbstractTextField.java | 2 +- server/src/com/vaadin/ui/Field.java | 22 +++++++ server/src/com/vaadin/ui/Form.java | 9 ++- server/src/com/vaadin/ui/RichTextArea.java | 2 +- .../src/com/vaadin/ui/AbstractSelectTest.java | 71 ++++++++++++++++++++++ .../tests/src/com/vaadin/ui/RichTextAreaTest.java | 47 ++++++++++++++ server/tests/src/com/vaadin/ui/TextAreaTest.java | 47 ++++++++++++++ 9 files changed, 200 insertions(+), 21 deletions(-) create mode 100644 server/tests/src/com/vaadin/ui/RichTextAreaTest.java create mode 100644 server/tests/src/com/vaadin/ui/TextAreaTest.java diff --git a/server/src/com/vaadin/ui/AbstractField.java b/server/src/com/vaadin/ui/AbstractField.java index 369ad1253c..df7bbb68a2 100644 --- a/server/src/com/vaadin/ui/AbstractField.java +++ b/server/src/com/vaadin/ui/AbstractField.java @@ -1502,25 +1502,12 @@ public abstract class AbstractField extends AbstractComponent implements markAsDirty(); } - /** - * Is the field empty? - * - * In general, "empty" state is same as null. As an exception, TextField - * also treats empty string as "empty". - */ - protected boolean isEmpty() { + @Override + public boolean isEmpty() { return (getFieldValue() == null); } - /** - * Clear the value of the field. - *

- * The field value is typically reset to the initial value of the field but - * this is not mandatory. Calling {@link #isEmpty()} on a cleared field must - * always returns true. - * - * @since - */ + @Override public void clear() { setValue(null); } diff --git a/server/src/com/vaadin/ui/AbstractSelect.java b/server/src/com/vaadin/ui/AbstractSelect.java index 2c4dd5be5d..70f08c95d8 100644 --- a/server/src/com/vaadin/ui/AbstractSelect.java +++ b/server/src/com/vaadin/ui/AbstractSelect.java @@ -1782,7 +1782,7 @@ public abstract class AbstractSelect extends AbstractField implements * @see AbstractField#isEmpty(). */ @Override - protected boolean isEmpty() { + public boolean isEmpty() { if (!multiSelect) { return super.isEmpty(); } else { diff --git a/server/src/com/vaadin/ui/AbstractTextField.java b/server/src/com/vaadin/ui/AbstractTextField.java index 9293d38119..ea0372bc8c 100644 --- a/server/src/com/vaadin/ui/AbstractTextField.java +++ b/server/src/com/vaadin/ui/AbstractTextField.java @@ -317,7 +317,7 @@ public abstract class AbstractTextField extends AbstractField implements } @Override - protected boolean isEmpty() { + public boolean isEmpty() { return super.isEmpty() || getValue().length() == 0; } diff --git a/server/src/com/vaadin/ui/Field.java b/server/src/com/vaadin/ui/Field.java index f191e1bdd7..6dee4de6cb 100644 --- a/server/src/com/vaadin/ui/Field.java +++ b/server/src/com/vaadin/ui/Field.java @@ -113,4 +113,26 @@ public interface Field extends Component, BufferedValidatable, Property, return (Property) getSource(); } } + + /** + * Is the field empty? + * + * In general, "empty" state is same as null. As an exception, TextField + * also treats empty string as "empty". + * + * @since + * @return true if the field is empty, false otherwise + */ + public boolean isEmpty(); + + /** + * Clears the value of the field. + *

+ * The field value is typically reset to the initial value of the field. + * Calling {@link #isEmpty()} on a cleared field must always returns true. + * + * @since + */ + public void clear(); + } diff --git a/server/src/com/vaadin/ui/Form.java b/server/src/com/vaadin/ui/Form.java index 48239b09e3..45532756e5 100644 --- a/server/src/com/vaadin/ui/Form.java +++ b/server/src/com/vaadin/ui/Form.java @@ -1186,9 +1186,14 @@ public class Form extends AbstractField implements Item.Editor, } } - /** Form is empty if all of its fields are empty. */ + /** + * {@inheritDoc} + *

+ * A Form is empty if all of its fields are empty. + * + */ @Override - protected boolean isEmpty() { + public boolean isEmpty() { for (Iterator> i = fields.values().iterator(); i.hasNext();) { Field f = i.next(); diff --git a/server/src/com/vaadin/ui/RichTextArea.java b/server/src/com/vaadin/ui/RichTextArea.java index 9d05181541..31327b3a6f 100644 --- a/server/src/com/vaadin/ui/RichTextArea.java +++ b/server/src/com/vaadin/ui/RichTextArea.java @@ -285,7 +285,7 @@ public class RichTextArea extends AbstractField implements } @Override - protected boolean isEmpty() { + public boolean isEmpty() { return super.isEmpty() || getValue().length() == 0; } diff --git a/server/tests/src/com/vaadin/ui/AbstractSelectTest.java b/server/tests/src/com/vaadin/ui/AbstractSelectTest.java index 83d66ee9d5..bd399f088c 100644 --- a/server/tests/src/com/vaadin/ui/AbstractSelectTest.java +++ b/server/tests/src/com/vaadin/ui/AbstractSelectTest.java @@ -16,10 +16,13 @@ package com.vaadin.ui; import java.util.ArrayList; +import java.util.HashSet; import org.junit.Assert; import org.junit.Test; +import com.vaadin.data.util.ObjectProperty; + public class AbstractSelectTest { @Test @@ -73,4 +76,72 @@ public class AbstractSelectTest { .toArray()); } + + @Test + public void singleSelectInitiallyEmpty() { + AbstractSelect s = new ListSelect(); + Assert.assertTrue(s.isEmpty()); + } + + @Test + public void singleSelectEmptyAfterClearUsingPDS() { + AbstractSelect s = new ListSelect(); + s.addItem("foo"); + s.addItem("bar"); + s.setPropertyDataSource(new ObjectProperty("foo")); + + Assert.assertFalse(s.isEmpty()); + s.clear(); + Assert.assertTrue(s.isEmpty()); + } + + @Test + public void singleSelectEmptyAfterClear() { + AbstractSelect s = new ListSelect(); + s.addItem("foo"); + s.addItem("bar"); + s.setValue("bar"); + + Assert.assertFalse(s.isEmpty()); + s.clear(); + Assert.assertTrue(s.isEmpty()); + } + + @Test + public void multiSelectInitiallyEmpty() { + AbstractSelect s = new ListSelect(); + s.setMultiSelect(true); + Assert.assertTrue(s.isEmpty()); + } + + @Test + public void multiSelectEmptyAfterClearUsingPDS() { + AbstractSelect s = new ListSelect(); + s.setMultiSelect(true); + s.addItem("foo"); + s.addItem("bar"); + HashSet sel = new HashSet(); + sel.add("foo"); + sel.add("bar"); + s.setPropertyDataSource(new ObjectProperty(sel)); + + Assert.assertFalse(s.isEmpty()); + s.clear(); + Assert.assertTrue(s.isEmpty()); + } + + @Test + public void multiSelectEmptyAfterClear() { + AbstractSelect s = new ListSelect(); + s.setMultiSelect(true); + s.addItem("foo"); + s.addItem("bar"); + s.select("foo"); + s.select("bar"); + + Assert.assertFalse(s.isEmpty()); + s.clear(); + Assert.assertTrue(s.isEmpty()); + } + } diff --git a/server/tests/src/com/vaadin/ui/RichTextAreaTest.java b/server/tests/src/com/vaadin/ui/RichTextAreaTest.java new file mode 100644 index 0000000000..ce0dfdc696 --- /dev/null +++ b/server/tests/src/com/vaadin/ui/RichTextAreaTest.java @@ -0,0 +1,47 @@ +/* + * 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.ui; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.data.util.ObjectProperty; + +public class RichTextAreaTest { + @Test + public void initiallyEmpty() { + RichTextArea tf = new RichTextArea(); + Assert.assertTrue(tf.isEmpty()); + } + + @Test + public void emptyAfterClearUsingPDS() { + RichTextArea tf = new RichTextArea(new ObjectProperty("foo")); + Assert.assertFalse(tf.isEmpty()); + tf.clear(); + Assert.assertTrue(tf.isEmpty()); + } + + @Test + public void emptyAfterClear() { + RichTextArea tf = new RichTextArea(); + tf.setValue("foobar"); + Assert.assertFalse(tf.isEmpty()); + tf.clear(); + Assert.assertTrue(tf.isEmpty()); + } + +} diff --git a/server/tests/src/com/vaadin/ui/TextAreaTest.java b/server/tests/src/com/vaadin/ui/TextAreaTest.java new file mode 100644 index 0000000000..e7e99c19e9 --- /dev/null +++ b/server/tests/src/com/vaadin/ui/TextAreaTest.java @@ -0,0 +1,47 @@ +/* + * 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.ui; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.data.util.ObjectProperty; + +public class TextAreaTest { + @Test + public void initiallyEmpty() { + TextArea tf = new TextArea(); + Assert.assertTrue(tf.isEmpty()); + } + + @Test + public void emptyAfterClearUsingPDS() { + TextArea tf = new TextArea(new ObjectProperty("foo")); + Assert.assertFalse(tf.isEmpty()); + tf.clear(); + Assert.assertTrue(tf.isEmpty()); + } + + @Test + public void emptyAfterClear() { + TextArea tf = new TextArea(); + tf.setValue("foobar"); + Assert.assertFalse(tf.isEmpty()); + tf.clear(); + Assert.assertTrue(tf.isEmpty()); + } + +} -- cgit v1.2.3 From 23bdb6e90d970bd4e9ef11c79c095e65b6963414 Mon Sep 17 00:00:00 2001 From: Denis Anisimov Date: Tue, 23 Sep 2014 22:18:40 +0300 Subject: Notify server side component about clicks in form layout (#6346). Change-Id: Idc4399dc94b253694c4e0d716d2b65964a2217f8 --- .../client/ui/formlayout/FormLayoutConnector.java | 24 ++++++++ .../formlayout/FormLayoutClickListener.java | 69 ++++++++++++++++++++++ .../formlayout/FormLayoutClickListenerTest.java | 62 +++++++++++++++++++ 3 files changed, 155 insertions(+) create mode 100644 uitest/src/com/vaadin/tests/components/formlayout/FormLayoutClickListener.java create mode 100644 uitest/src/com/vaadin/tests/components/formlayout/FormLayoutClickListenerTest.java diff --git a/client/src/com/vaadin/client/ui/formlayout/FormLayoutConnector.java b/client/src/com/vaadin/client/ui/formlayout/FormLayoutConnector.java index 494a1a87ff..ae4b03e0f8 100644 --- a/client/src/com/vaadin/client/ui/formlayout/FormLayoutConnector.java +++ b/client/src/com/vaadin/client/ui/formlayout/FormLayoutConnector.java @@ -24,18 +24,41 @@ import com.vaadin.client.Util; import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.ui.AbstractFieldConnector; import com.vaadin.client.ui.AbstractLayoutConnector; +import com.vaadin.client.ui.LayoutClickEventHandler; import com.vaadin.client.ui.VFormLayout; import com.vaadin.client.ui.VFormLayout.Caption; import com.vaadin.client.ui.VFormLayout.ErrorFlag; import com.vaadin.client.ui.VFormLayout.VFormLayoutTable; import com.vaadin.shared.ui.Connect; +import com.vaadin.shared.ui.LayoutClickRpc; import com.vaadin.shared.ui.MarginInfo; +import com.vaadin.shared.ui.orderedlayout.AbstractOrderedLayoutServerRpc; import com.vaadin.shared.ui.orderedlayout.AbstractOrderedLayoutState; import com.vaadin.ui.FormLayout; @Connect(FormLayout.class) public class FormLayoutConnector extends AbstractLayoutConnector { + /* + * Handlers & Listeners + */ + + private LayoutClickEventHandler clickEventHandler = new LayoutClickEventHandler( + this) { + + @Override + protected ComponentConnector getChildComponent( + com.google.gwt.user.client.Element element) { + return Util.getConnectorForElement(getConnection(), getWidget(), + element); + } + + @Override + protected LayoutClickRpc getLayoutClickRPC() { + return getRpcProxy(AbstractOrderedLayoutServerRpc.class); + } + }; + @Override public AbstractOrderedLayoutState getState() { return (AbstractOrderedLayoutState) super.getState(); @@ -45,6 +68,7 @@ public class FormLayoutConnector extends AbstractLayoutConnector { public void onStateChanged(StateChangeEvent stateChangeEvent) { super.onStateChanged(stateChangeEvent); + clickEventHandler.handleEventHandlerRegistration(); VFormLayoutTable formLayoutTable = getWidget().table; formLayoutTable.setMargins(new MarginInfo(getState().marginsBitmask)); diff --git a/uitest/src/com/vaadin/tests/components/formlayout/FormLayoutClickListener.java b/uitest/src/com/vaadin/tests/components/formlayout/FormLayoutClickListener.java new file mode 100644 index 0000000000..35b8d9ca86 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/formlayout/FormLayoutClickListener.java @@ -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.formlayout; + +import com.vaadin.event.LayoutEvents.LayoutClickEvent; +import com.vaadin.event.LayoutEvents.LayoutClickListener; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUIWithLog; +import com.vaadin.ui.FormLayout; +import com.vaadin.ui.Label; + +/** + * Test UI for Form layout click listener. + * + * @author Vaadin Ltd + */ +public class FormLayoutClickListener extends AbstractTestUIWithLog { + + @Override + protected void setup(VaadinRequest request) { + FormLayout layout = new FormLayout(); + + layout.addStyleName("form"); + + Label label = new Label("target"); + label.addStyleName("label"); + layout.addComponent(label); + + layout.addLayoutClickListener(new LayoutClickListener() { + + @Override + public void layoutClick(LayoutClickEvent event) { + log("Child component: " + + (event.getChildComponent() == null ? null : event + .getChildComponent().getStyleName())); + log("Clicked component: " + + (event.getClickedComponent() == null ? null : event + .getClickedComponent().getStyleName())); + log("Source component: " + event.getComponent().getStyleName()); + } + }); + + addComponent(layout); + } + + @Override + protected String getTestDescription() { + return "LayoutClickListener should work in FormLayout"; + } + + @Override + protected Integer getTicketNumber() { + return 6346; + } + +} diff --git a/uitest/src/com/vaadin/tests/components/formlayout/FormLayoutClickListenerTest.java b/uitest/src/com/vaadin/tests/components/formlayout/FormLayoutClickListenerTest.java new file mode 100644 index 0000000000..bcab514a5d --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/formlayout/FormLayoutClickListenerTest.java @@ -0,0 +1,62 @@ +/* + * 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.formlayout; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.openqa.selenium.By; + +import com.vaadin.testbench.elements.FormLayoutElement; +import com.vaadin.tests.tb3.MultiBrowserTest; + +/** + * Test for form layout click listener. + * + * @author Vaadin Ltd + */ +public class FormLayoutClickListenerTest extends MultiBrowserTest { + + @Before + public void setUp() { + openTestURL(); + } + + @Test + public void layoutClickListener_clickOnLayout_childAndClickedComponentsAreNull() { + $(FormLayoutElement.class).first().click(); + + Assert.assertEquals("Source component for click event must be form", + "3. Source component: form", getLogRow(0)); + Assert.assertEquals("Clicked component for click event must be null", + "2. Clicked component: null", getLogRow(1)); + Assert.assertEquals("Child component for click event must be null", + "1. Child component: null", getLogRow(2)); + } + + @Test + public void layoutClickListener_clickOnLabel_lableIsChildAndClickedComponent() { + findElement(By.className("label")).click(); + + Assert.assertEquals("Source component for click event must be form", + "3. Source component: form", getLogRow(0)); + Assert.assertEquals("Clicked component for click event must be label", + "2. Clicked component: label", getLogRow(1)); + Assert.assertEquals("Child component for click event must be label", + "1. Child component: label", getLogRow(2)); + } + +} -- cgit v1.2.3 From b91063f884108bfc69a617846d91263339b062fe Mon Sep 17 00:00:00 2001 From: Artur Signell Date: Tue, 9 Dec 2014 19:43:06 +0200 Subject: Clear all bound fields when nulling the field group data source (#12371) Change-Id: I48eb0e7851187fa6bbb9024730b2611a74501ad0 --- .../com/vaadin/data/fieldgroup/BeanFieldGroup.java | 9 +- .../src/com/vaadin/data/fieldgroup/FieldGroup.java | 19 ++-- .../component/fieldgroup/BeanFieldGroupTest.java | 12 +-- .../src/com/vaadin/tests/fieldgroup/BasicCrud.java | 110 +++++++++++++++++++++ .../com/vaadin/tests/fieldgroup/BasicCrudTest.java | 68 +++++++++++++ 5 files changed, 198 insertions(+), 20 deletions(-) create mode 100644 uitest/src/com/vaadin/tests/fieldgroup/BasicCrud.java create mode 100644 uitest/src/com/vaadin/tests/fieldgroup/BasicCrudTest.java diff --git a/server/src/com/vaadin/data/fieldgroup/BeanFieldGroup.java b/server/src/com/vaadin/data/fieldgroup/BeanFieldGroup.java index 1f3ee36d58..257a958f3a 100644 --- a/server/src/com/vaadin/data/fieldgroup/BeanFieldGroup.java +++ b/server/src/com/vaadin/data/fieldgroup/BeanFieldGroup.java @@ -125,12 +125,19 @@ public class BeanFieldGroup extends FieldGroup { * Helper method for setting the data source directly using a bean. This * method wraps the bean in a {@link BeanItem} and calls * {@link #setItemDataSource(Item)}. + *

+ * For null values, a null item is passed to + * {@link #setItemDataSource(Item)} to be properly clear fields. * * @param bean * The bean to use as data source. */ public void setItemDataSource(T bean) { - setItemDataSource(new BeanItem(bean, beanType)); + if (bean == null) { + setItemDataSource((Item) null); + } else { + setItemDataSource(new BeanItem(bean, beanType)); + } } @Override diff --git a/server/src/com/vaadin/data/fieldgroup/FieldGroup.java b/server/src/com/vaadin/data/fieldgroup/FieldGroup.java index bcfa8395df..c89b94ace9 100644 --- a/server/src/com/vaadin/data/fieldgroup/FieldGroup.java +++ b/server/src/com/vaadin/data/fieldgroup/FieldGroup.java @@ -26,7 +26,6 @@ import java.util.List; import com.vaadin.data.Item; import com.vaadin.data.Property; -import com.vaadin.data.Validator; import com.vaadin.data.Validator.InvalidValueException; import com.vaadin.data.util.TransactionalPropertyWrapper; import com.vaadin.ui.AbstractField; @@ -256,6 +255,9 @@ public class FieldGroup implements Serializable { fieldToPropertyId.put(field, propertyId); propertyIdToField.put(propertyId, field); if (itemDataSource == null) { + // Clear any possible existing binding to clear the field + field.setPropertyDataSource(null); + field.clear(); // Will be bound when data source is set return; } @@ -461,7 +463,7 @@ public class FieldGroup implements Serializable { List invalidValueExceptions = commitFields(); - if(invalidValueExceptions.isEmpty()) { + if (invalidValueExceptions.isEmpty()) { firePostCommitEvent(); commitTransactions(); } else { @@ -489,12 +491,14 @@ public class FieldGroup implements Serializable { return invalidValueExceptions; } - private void throwInvalidValueException(List invalidValueExceptions) { - if(invalidValueExceptions.size() == 1) { + private void throwInvalidValueException( + List invalidValueExceptions) { + if (invalidValueExceptions.size() == 1) { throw invalidValueExceptions.get(0); } else { - InvalidValueException[] causes = invalidValueExceptions.toArray( - new InvalidValueException[invalidValueExceptions.size()]); + InvalidValueException[] causes = invalidValueExceptions + .toArray(new InvalidValueException[invalidValueExceptions + .size()]); throw new InvalidValueException(null, causes); } @@ -515,8 +519,7 @@ public class FieldGroup implements Serializable { private void commitTransactions() { for (Field f : fieldToPropertyId.keySet()) { - ((Property.Transactional) f.getPropertyDataSource()) - .commit(); + ((Property.Transactional) f.getPropertyDataSource()).commit(); } } diff --git a/server/tests/src/com/vaadin/tests/server/component/fieldgroup/BeanFieldGroupTest.java b/server/tests/src/com/vaadin/tests/server/component/fieldgroup/BeanFieldGroupTest.java index 965fb49479..9c37b91ef5 100644 --- a/server/tests/src/com/vaadin/tests/server/component/fieldgroup/BeanFieldGroupTest.java +++ b/server/tests/src/com/vaadin/tests/server/component/fieldgroup/BeanFieldGroupTest.java @@ -2,8 +2,6 @@ package com.vaadin.tests.server.component.fieldgroup; import static org.junit.Assert.assertEquals; -import java.util.Collection; - import org.junit.Assert; import org.junit.Test; @@ -144,15 +142,7 @@ public class BeanFieldGroupTest { group.setItemDataSource((MyBean) null); BeanItem dataSource = group.getItemDataSource(); - Assert.assertNotNull("Data source is null for null bean", dataSource); - - Collection itemPropertyIds = dataSource.getItemPropertyIds(); - Assert.assertEquals("Unexpected number of properties", 3, - itemPropertyIds.size()); - for (Object id : itemPropertyIds) { - Assert.assertNull("Value for property " + id + " is not null", - dataSource.getItemProperty(id).getValue()); - } + Assert.assertNull("Data source is null for null bean", dataSource); } @Test diff --git a/uitest/src/com/vaadin/tests/fieldgroup/BasicCrud.java b/uitest/src/com/vaadin/tests/fieldgroup/BasicCrud.java new file mode 100644 index 0000000000..be0368f4ae --- /dev/null +++ b/uitest/src/com/vaadin/tests/fieldgroup/BasicCrud.java @@ -0,0 +1,110 @@ +/* + * 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.fieldgroup; + +import com.vaadin.annotations.Theme; +import com.vaadin.data.Property.ValueChangeEvent; +import com.vaadin.data.Property.ValueChangeListener; +import com.vaadin.data.fieldgroup.BeanFieldGroup; +import com.vaadin.data.fieldgroup.PropertyId; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.util.Person; +import com.vaadin.tests.util.PersonContainer; +import com.vaadin.ui.HorizontalLayout; +import com.vaadin.ui.Table; +import com.vaadin.ui.TextField; +import com.vaadin.ui.UI; +import com.vaadin.ui.VerticalLayout; + +@Theme("valo") +public class BasicCrud extends UI { + + private Form form; + + @Override + protected void init(VaadinRequest request) { + VerticalLayout main = new VerticalLayout(); + main.setMargin(true); + main.setSpacing(true); + final Table t = new Table(); + // t.setSelectionMode(SelectionMode.SINGLE); + t.setSelectable(true); + + PersonContainer c = PersonContainer.createWithTestData(); + + c.addBean(new Person("first", "Last", "email", "phone", "street", + 12332, "Turku")); + c.addBean(new Person("Foo", "Bar", "me@some.where", "123", + "homestreet 12", 10000, "Glasgow")); + t.setContainerDataSource(c); + // t.removeColumn("address"); + // t.setColumnOrder("firstName", "lastName", "email", "phoneNumber", + // "address.streetAddress", "address.postalCode", "address.city"); + t.setVisibleColumns("firstName", "lastName", "email", "phoneNumber", + "address.streetAddress", "address.postalCode", "address.city"); + + // t.addSelectionChangeListener(new SelectionChangeListener() { + // @Override + // public void selectionChange(SelectionChangeEvent event) { + // form.edit((Person) t.getSelectedRow()); + // } + // }); + t.addValueChangeListener(new ValueChangeListener() { + + @Override + public void valueChange(ValueChangeEvent event) { + form.edit((Person) t.getValue()); + } + }); + + form = new Form(); + + t.setSizeFull(); + + main.setSizeFull(); + main.addComponent(t); + main.addComponent(form); + main.setExpandRatio(t, 1); + setContent(main); + } + + public static class Form extends HorizontalLayout { + private TextField firstName = new TextField("First name"); + private TextField lastName = new TextField("Last name"); + private TextField email = new TextField("E-mail"); + @PropertyId("address.streetAddress") + private TextField streetAddress = new TextField("Street address"); + @PropertyId("address.postalCode") + private TextField postalCode = new TextField("Postal code"); + + BeanFieldGroup fieldGroup = new BeanFieldGroup( + Person.class); + + public Form() { + setSpacing(true); + setId("form"); + fieldGroup.bindMemberFields(this); + + // Stupid integer binding + postalCode.setNullRepresentation(""); + addComponents(firstName, lastName, email, streetAddress, postalCode); + } + + public void edit(Person p) { + fieldGroup.setItemDataSource(p); + } + } +} diff --git a/uitest/src/com/vaadin/tests/fieldgroup/BasicCrudTest.java b/uitest/src/com/vaadin/tests/fieldgroup/BasicCrudTest.java new file mode 100644 index 0000000000..a41725dbc9 --- /dev/null +++ b/uitest/src/com/vaadin/tests/fieldgroup/BasicCrudTest.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.fieldgroup; + +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.testbench.elements.AbstractOrderedLayoutElement; +import com.vaadin.testbench.elements.TableElement; +import com.vaadin.testbench.elements.TextFieldElement; +import com.vaadin.tests.tb3.SingleBrowserTest; + +public class BasicCrudTest extends SingleBrowserTest { + + @Test + public void fieldsInitiallyEmpty() { + openTestURL(); + AbstractOrderedLayoutElement fieldsLayout = $( + AbstractOrderedLayoutElement.class).id("form"); + List textFields = fieldsLayout.$( + TextFieldElement.class).all(); + + for (TextFieldElement e : textFields) { + Assert.assertEquals("TextField should be empty", "", e.getValue()); + } + } + + @Test + public void fieldsClearedOnDeselect() { + openTestURL(); + AbstractOrderedLayoutElement fieldsLayout = $( + AbstractOrderedLayoutElement.class).id("form"); + + // Select row + $(TableElement.class).first().getCell(2, 2).click(); + + List textFields = fieldsLayout.$( + TextFieldElement.class).all(); + + for (TextFieldElement e : textFields) { + Assert.assertNotEquals("TextField should not be empty", "", + e.getValue()); + } + + // Deselect row + $(TableElement.class).first().getCell(2, 2).click(); + + for (TextFieldElement e : textFields) { + Assert.assertEquals("TextField should be empty", "", e.getValue()); + } + + } +} -- cgit v1.2.3 From c4e4f449464f22c11e33b92f8aa578db959e92b2 Mon Sep 17 00:00:00 2001 From: Artur Signell Date: Tue, 9 Dec 2014 22:16:50 +0200 Subject: Convert empty string to null Enum value, only throw ConversionExceptions (#14756) Change-Id: I027a245975db12e3661740bd233edd98e73994e9 --- .../data/util/converter/StringToEnumConverter.java | 20 +++++++++++--------- .../data/converter/TestStringToEnumConverter.java | 14 ++++++++++++++ .../tests/components/textfield/EnumTextField.java | 1 + .../components/textfield/EnumTextFieldTest.java | 7 ++++--- 4 files changed, 30 insertions(+), 12 deletions(-) diff --git a/server/src/com/vaadin/data/util/converter/StringToEnumConverter.java b/server/src/com/vaadin/data/util/converter/StringToEnumConverter.java index a1328d831c..29bf8fc400 100644 --- a/server/src/com/vaadin/data/util/converter/StringToEnumConverter.java +++ b/server/src/com/vaadin/data/util/converter/StringToEnumConverter.java @@ -34,7 +34,7 @@ public class StringToEnumConverter implements Converter { @Override public Enum convertToModel(String value, Class targetType, Locale locale) throws ConversionException { - if (value == null) { + if (value == null || value.trim().equals("")) { return null; } if (locale == null) { @@ -46,19 +46,21 @@ public class StringToEnumConverter implements Converter { String result = value.replace(" ", "_").toUpperCase(locale); try { return Enum.valueOf(targetType, result); - } catch (IllegalArgumentException ee) { + } catch (Exception ee) { // There was no match. Try to compare the available values to see if // the constant is using something else than all upper case - - EnumSet set = EnumSet.allOf(targetType); - for (Enum e : set) { - if (e.name().toUpperCase(locale).equals(result)) { - return e; + try { + EnumSet set = EnumSet.allOf(targetType); + for (Enum e : set) { + if (e.name().toUpperCase(locale).equals(result)) { + return e; + } } + } catch (Exception e) { } - // Fallback did not work either, re-throw original exception so user - // knows what went wrong + // Fallback did not work either, re-throw original exception so + // user knows what went wrong throw new ConversionException(ee); } } diff --git a/server/tests/src/com/vaadin/tests/data/converter/TestStringToEnumConverter.java b/server/tests/src/com/vaadin/tests/data/converter/TestStringToEnumConverter.java index 2b19395c08..5dc24ca43a 100644 --- a/server/tests/src/com/vaadin/tests/data/converter/TestStringToEnumConverter.java +++ b/server/tests/src/com/vaadin/tests/data/converter/TestStringToEnumConverter.java @@ -3,6 +3,7 @@ package com.vaadin.tests.data.converter; import junit.framework.TestCase; import com.vaadin.data.util.converter.Converter; +import com.vaadin.data.util.converter.Converter.ConversionException; import com.vaadin.data.util.converter.ReverseConverter; import com.vaadin.data.util.converter.StringToEnumConverter; @@ -16,6 +17,19 @@ public class TestStringToEnumConverter extends TestCase { Converter reverseConverter = new ReverseConverter( converter); + public void testEmptyStringConversion() { + assertEquals(null, converter.convertToModel("", Enum.class, null)); + } + + public void testInvalidEnumClassConversion() { + try { + converter.convertToModel("Foo", Enum.class, null); + fail("No exception thrown"); + } catch (ConversionException e) { + // OK + } + } + public void testNullConversion() { assertEquals(null, converter.convertToModel(null, Enum.class, null)); } diff --git a/uitest/src/com/vaadin/tests/components/textfield/EnumTextField.java b/uitest/src/com/vaadin/tests/components/textfield/EnumTextField.java index 67b3b84688..99e08ae32d 100644 --- a/uitest/src/com/vaadin/tests/components/textfield/EnumTextField.java +++ b/uitest/src/com/vaadin/tests/components/textfield/EnumTextField.java @@ -31,6 +31,7 @@ public class EnumTextField extends AbstractTestUIWithLog { @Override protected void setup(VaadinRequest request) { final TextField tf = new TextField(); + tf.setNullRepresentation(""); tf.addValueChangeListener(new ValueChangeListener() { @Override diff --git a/uitest/src/com/vaadin/tests/components/textfield/EnumTextFieldTest.java b/uitest/src/com/vaadin/tests/components/textfield/EnumTextFieldTest.java index 113acee3a2..fe26fb7aad 100644 --- a/uitest/src/com/vaadin/tests/components/textfield/EnumTextFieldTest.java +++ b/uitest/src/com/vaadin/tests/components/textfield/EnumTextFieldTest.java @@ -27,8 +27,7 @@ public class EnumTextFieldTest extends SingleBrowserTest { public void validValues() { openTestURL(); $(TextFieldElement.class).first().clear(); - $(TextFieldElement.class).first().sendKeys("Value"); - $(TextFieldElement.class).first().sendKeys(Keys.TAB); + $(TextFieldElement.class).first().sendKeys("Value", Keys.TAB); Assert.assertEquals("3. Value (valid)", getLogRow(0)); $(TextFieldElement.class).first().clear(); @@ -41,13 +40,15 @@ public class EnumTextFieldTest extends SingleBrowserTest { $(TextFieldElement.class).first().sendKeys(Keys.TAB); Assert.assertEquals("7. The last value (valid)", getLogRow(0)); + $(TextFieldElement.class).first().clear(); + Assert.assertEquals("8. null (valid)", getLogRow(0)); + } @Test public void invalidValue() { openTestURL(); $(TextFieldElement.class).first().clear(); - Assert.assertEquals("2. (INVALID)", getLogRow(0)); $(TextFieldElement.class).first().sendKeys("bar"); $(TextFieldElement.class).first().sendKeys(Keys.TAB); -- cgit v1.2.3 From 5eaf32a1404399f7b39f3b60060ed3d101ec08f8 Mon Sep 17 00:00:00 2001 From: Alexey Fansky Date: Wed, 10 Dec 2014 15:52:46 -0800 Subject: Reset tabsheet scroller when unhiding leftmost tab (#14644) Checking whether invisible tab are present before the scroll index and if so resetting the scroll index when tab goes back to visible. Change-Id: I156dbe93963b0d9f46e21a593ffc3c4c77ebd1c6 --- client/src/com/vaadin/client/ui/VTabsheet.java | 27 ++++++++++++- .../tabsheet/FirstTabNotVisibleInTabsheet.java | 44 ++++++++++++++++++++++ .../tabsheet/FirstTabNotVisibleInTabsheetTest.java | 27 +++++++++++++ 3 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 uitest/src/com/vaadin/tests/components/tabsheet/FirstTabNotVisibleInTabsheet.java create mode 100644 uitest/src/com/vaadin/tests/components/tabsheet/FirstTabNotVisibleInTabsheetTest.java diff --git a/client/src/com/vaadin/client/ui/VTabsheet.java b/client/src/com/vaadin/client/ui/VTabsheet.java index bcca117395..090f83c066 100644 --- a/client/src/com/vaadin/client/ui/VTabsheet.java +++ b/client/src/com/vaadin/client/ui/VTabsheet.java @@ -78,6 +78,8 @@ import com.vaadin.shared.ui.tabsheet.TabsheetState; public class VTabsheet extends VTabsheetBase implements Focusable, SubPartAware { + private static final String PREV_SCROLLER_DISABLED_CLASSNAME = "Prev-disabled"; + private static class VCloseEvent { private Tab tab; @@ -1069,6 +1071,22 @@ public class VTabsheet extends VTabsheetBase implements Focusable, SubPartAware updateOpenTabSize(); } + private boolean isAllTabsBeforeIndexInvisible() { + boolean invisible = true; + for (int i = 0; i < scrollerIndex; i++) { + invisible = invisible & !tb.getTab(i).isVisible(); + } + return invisible; + } + + private boolean isScrollerPrevDisabled() { + return scrollerPrev.getClassName().contains(PREV_SCROLLER_DISABLED_CLASSNAME); + } + + private boolean isIndexSkippingHiddenTabs() { + return isAllTabsBeforeIndexInvisible() && isScrollerPrevDisabled(); + } + @Override public void renderTab(final TabState tabState, int index) { Tab tab = tb.getTab(index); @@ -1080,10 +1098,15 @@ public class VTabsheet extends VTabsheetBase implements Focusable, SubPartAware tab.setEnabledOnServer((!disabledTabKeys.contains(tabKeys.get(index)))); tab.setHiddenOnServer(!tabState.visible); - if (scrolledOutOfView(index)) { + if (scrolledOutOfView(index) && !isIndexSkippingHiddenTabs()) { // Should not set tabs visible if they are scrolled out of view tab.setVisible(false); } else { + //reset the scroller index back to zero if tab is visible + //again and tab is in view + if(isIndexSkippingHiddenTabs() && tabState.visible) { + scrollerIndex = 0; + } tab.setVisible(tabState.visible); } @@ -1223,7 +1246,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable, SubPartAware if (tb.getTabCount() > 0 && tb.isVisible() && (scrolled || clipped)) { scroller.getStyle().clearDisplay(); DOM.setElementProperty(scrollerPrev, "className", - SCROLLER_CLASSNAME + (scrolled ? "Prev" : "Prev-disabled")); + SCROLLER_CLASSNAME + (scrolled ? "Prev" : PREV_SCROLLER_DISABLED_CLASSNAME)); DOM.setElementProperty(scrollerNext, "className", SCROLLER_CLASSNAME + (clipped ? "Next" : "Next-disabled")); diff --git a/uitest/src/com/vaadin/tests/components/tabsheet/FirstTabNotVisibleInTabsheet.java b/uitest/src/com/vaadin/tests/components/tabsheet/FirstTabNotVisibleInTabsheet.java new file mode 100644 index 0000000000..c21c702bd0 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/tabsheet/FirstTabNotVisibleInTabsheet.java @@ -0,0 +1,44 @@ +package com.vaadin.tests.components.tabsheet; + +import com.vaadin.annotations.VaadinServletConfiguration; +import com.vaadin.server.VaadinRequest; +import com.vaadin.server.VaadinServlet; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.*; + +import javax.servlet.annotation.WebServlet; + +@SuppressWarnings("serial") +public class FirstTabNotVisibleInTabsheet extends AbstractTestUI { + + private TabSheet.Tab firstTab; + + @Override + protected void setup(VaadinRequest request) { + TabSheet tabSheet = new TabSheet(); + tabSheet.setWidth("600px"); + + firstTab = tabSheet.addTab(new Label("first visible tab"), "first visible tab"); + + for (int i = 2; i < 10; i++) { + tabSheet.addTab(new Label("visible tab " + i), "visible tab " + i); + } + + addComponent(new VerticalLayout(tabSheet, new Button("Toggle first tab", new Button.ClickListener() { + @Override + public void buttonClick(Button.ClickEvent event) { + firstTab.setVisible(!firstTab.isVisible()); + } + }))); + } + + @Override + protected Integer getTicketNumber() { + return 14644; + } + + @Override + protected String getTestDescription() { + return "First tabsheet tab is not set visible back once it gets invisible"; + } +} \ No newline at end of file diff --git a/uitest/src/com/vaadin/tests/components/tabsheet/FirstTabNotVisibleInTabsheetTest.java b/uitest/src/com/vaadin/tests/components/tabsheet/FirstTabNotVisibleInTabsheetTest.java new file mode 100644 index 0000000000..e57651ba03 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/tabsheet/FirstTabNotVisibleInTabsheetTest.java @@ -0,0 +1,27 @@ +package com.vaadin.tests.components.tabsheet; + + +import com.vaadin.testbench.elements.ButtonElement; +import com.vaadin.testbench.elements.TabSheetElement; +import com.vaadin.tests.tb3.MultiBrowserTest; +import junit.framework.Assert; +import org.junit.Test; + +public class FirstTabNotVisibleInTabsheetTest extends MultiBrowserTest { + @Test + public void testFirstTabIsVisibleAfterBeingInvisible() { + openTestURL(); + + toggleFirstTabVisibility(); + toggleFirstTabVisibility(); + + TabSheetElement tabSheet = $(TabSheetElement.class).first(); + + Assert.assertTrue("TabSheet should have first tab visible", + tabSheet.getTabCaptions().contains("first visible tab")); + } + + private void toggleFirstTabVisibility() { + $(ButtonElement.class).caption("Toggle first tab").first().click(); + } +} -- cgit v1.2.3 From 862a2f74d9f137560fe62a33a5b23e4dd31aeeae Mon Sep 17 00:00:00 2001 From: Sergey Budkin Date: Fri, 7 Nov 2014 15:22:24 +0200 Subject: VBrowserDetails fails to detect Android when using Firefox (#15183) Changed detection logic. Change-Id: I9cb8e94fe6bb5be587fa73ed6a71d2c7c1d91733 --- .../com/vaadin/client/TestVBrowserDetailsUserAgentParser.java | 10 ++++++++++ shared/src/com/vaadin/shared/VBrowserDetails.java | 11 ++++------- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/client/tests/src/com/vaadin/client/TestVBrowserDetailsUserAgentParser.java b/client/tests/src/com/vaadin/client/TestVBrowserDetailsUserAgentParser.java index 6cd0630137..cb70fc7a39 100644 --- a/client/tests/src/com/vaadin/client/TestVBrowserDetailsUserAgentParser.java +++ b/client/tests/src/com/vaadin/client/TestVBrowserDetailsUserAgentParser.java @@ -10,6 +10,7 @@ public class TestVBrowserDetailsUserAgentParser extends TestCase { private static final String FIREFOX30_WINDOWS = "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-GB; rv:1.9.0.6) Gecko/2009011913 Firefox/3.0.6"; private static final String FIREFOX30_LINUX = "Mozilla/5.0 (X11; U; Linux x86_64; es-ES; rv:1.9.0.12) Gecko/2009070811 Ubuntu/9.04 (jaunty) Firefox/3.0.12"; + private static final String FIREFOX33_ANDROID = "Mozilla/5.0 (Android; Tablet; rv:33.0) Gecko/33.0 Firefox/33.0"; private static final String FIREFOX35_WINDOWS = "Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1.8) Gecko/20100202 Firefox/3.5.8 (.NET CLR 3.5.30729) FirePHP/0.4"; private static final String FIREFOX36_WINDOWS = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6 (.NET CLR 3.5.30729)"; private static final String FIREFOX36B_MAC = "UAString mozilla/5.0 (macintosh; u; intel mac os x 10.6; en-us; rv:1.9.2) gecko/20100115 firefox/3.6"; @@ -209,6 +210,15 @@ public class TestVBrowserDetailsUserAgentParser extends TestCase { assertLinux(bd); } + public void testFirefox33Android() { + VBrowserDetails bd = new VBrowserDetails(FIREFOX33_ANDROID); + assertGecko(bd); + assertFirefox(bd); + assertBrowserMajorVersion(bd, 33); + assertBrowserMinorVersion(bd, 0); + assertAndroid(bd, -1, -1); + } + public void testFirefox35() { VBrowserDetails bd = new VBrowserDetails(FIREFOX35_WINDOWS); assertGecko(bd); diff --git a/shared/src/com/vaadin/shared/VBrowserDetails.java b/shared/src/com/vaadin/shared/VBrowserDetails.java index fa21bddc96..6e45d33e16 100644 --- a/shared/src/com/vaadin/shared/VBrowserDetails.java +++ b/shared/src/com/vaadin/shared/VBrowserDetails.java @@ -167,14 +167,11 @@ public class VBrowserDetails implements Serializable { if (userAgent.contains("windows ")) { os = OperatingSystem.WINDOWS; isWindowsPhone = userAgent.contains("windows phone"); + } else if (userAgent.contains("android")) { + os = OperatingSystem.ANDROID; + parseAndroidVersion(userAgent); } else if (userAgent.contains("linux")) { - if (userAgent.contains("android")) { - os = OperatingSystem.ANDROID; - parseAndroidVersion(userAgent); - } else { - os = OperatingSystem.LINUX; - - } + os = OperatingSystem.LINUX; } else if (userAgent.contains("macintosh") || userAgent.contains("mac osx") || userAgent.contains("mac os x")) { -- cgit v1.2.3 From c0f689891ca05e9e74d184ee7528e1aadde84023 Mon Sep 17 00:00:00 2001 From: Artur Signell Date: Thu, 4 Dec 2014 09:13:15 +0200 Subject: Test UI for #15321 Change-Id: I108f8ad473e6637bff9619f110f9194a56482c3e --- .../HorizontalLayoutWithLabelAndButton.java | 43 ++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 uitest/src/com/vaadin/tests/layouts/HorizontalLayoutWithLabelAndButton.java diff --git a/uitest/src/com/vaadin/tests/layouts/HorizontalLayoutWithLabelAndButton.java b/uitest/src/com/vaadin/tests/layouts/HorizontalLayoutWithLabelAndButton.java new file mode 100644 index 0000000000..9381f2caeb --- /dev/null +++ b/uitest/src/com/vaadin/tests/layouts/HorizontalLayoutWithLabelAndButton.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.layouts; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUIWithLog; +import com.vaadin.ui.Button; +import com.vaadin.ui.HorizontalLayout; +import com.vaadin.ui.Label; + +public class HorizontalLayoutWithLabelAndButton extends AbstractTestUIWithLog { + + @Override + protected void setup(VaadinRequest request) { + HorizontalLayout hl = new HorizontalLayout(); + hl.setSpacing(true); + hl.setWidth("100%"); + Label l = new Label(); + l.setCaption("POTUS Database"); + l.setSizeUndefined(); + + Button b = new Button("Add new"); + hl.addComponents(l, b); + b.setStyleName("primary"); + hl.setExpandRatio(b, 1); + + addComponent(hl); + } + +} -- cgit v1.2.3 From 4e477009c14231b5ecbd6bd0d289ac88eace2391 Mon Sep 17 00:00:00 2001 From: Artur Signell Date: Wed, 10 Dec 2014 23:41:45 +0200 Subject: Validate input to FileResource constructor (#14253) Change-Id: I7789f2de4cb179dfff936498f58dd6f2e491689b --- server/src/com/vaadin/server/FileResource.java | 3 ++ .../com/vaadin/tests/server/FileResourceTest.java | 36 ++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 server/tests/src/com/vaadin/tests/server/FileResourceTest.java diff --git a/server/src/com/vaadin/server/FileResource.java b/server/src/com/vaadin/server/FileResource.java index de14e2f0f2..b32905f972 100644 --- a/server/src/com/vaadin/server/FileResource.java +++ b/server/src/com/vaadin/server/FileResource.java @@ -57,6 +57,9 @@ public class FileResource implements ConnectorResource { * the file that should be served. */ public FileResource(File sourceFile) { + if (sourceFile == null) { + throw new IllegalArgumentException("File cannot be null"); + } setSourceFile(sourceFile); } diff --git a/server/tests/src/com/vaadin/tests/server/FileResourceTest.java b/server/tests/src/com/vaadin/tests/server/FileResourceTest.java new file mode 100644 index 0000000000..4798fb9f05 --- /dev/null +++ b/server/tests/src/com/vaadin/tests/server/FileResourceTest.java @@ -0,0 +1,36 @@ +/* + * 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.server; + +import java.io.File; + +import org.junit.Test; + +import com.vaadin.server.FileResource; + +public class FileResourceTest { + + @Test(expected = IllegalArgumentException.class) + public void nullFile() { + new FileResource(null); + } + + @Test(expected = RuntimeException.class) + public void nonExistingFile() { + new FileResource(new File("nonexisting")).getStream(); + } + +} -- cgit v1.2.3 From dd879668f5f74dfea8a804f3ff3c1e7c2f89a5a3 Mon Sep 17 00:00:00 2001 From: Markus Koivisto Date: Thu, 11 Dec 2014 12:10:48 +0000 Subject: Revert "Notify server side component about clicks in form layout (#6346)." This reverts commit 23bdb6e90d970bd4e9ef11c79c095e65b6963414. The included test (FormLayoutClickListenerTest) fails on all other browsers except PhantomJS. Change-Id: Ib60d051da9100eef8f30e6578ac73314bcda3ec3 --- .../client/ui/formlayout/FormLayoutConnector.java | 24 -------- .../formlayout/FormLayoutClickListener.java | 69 ---------------------- .../formlayout/FormLayoutClickListenerTest.java | 62 ------------------- 3 files changed, 155 deletions(-) delete mode 100644 uitest/src/com/vaadin/tests/components/formlayout/FormLayoutClickListener.java delete mode 100644 uitest/src/com/vaadin/tests/components/formlayout/FormLayoutClickListenerTest.java diff --git a/client/src/com/vaadin/client/ui/formlayout/FormLayoutConnector.java b/client/src/com/vaadin/client/ui/formlayout/FormLayoutConnector.java index ae4b03e0f8..494a1a87ff 100644 --- a/client/src/com/vaadin/client/ui/formlayout/FormLayoutConnector.java +++ b/client/src/com/vaadin/client/ui/formlayout/FormLayoutConnector.java @@ -24,41 +24,18 @@ import com.vaadin.client.Util; import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.ui.AbstractFieldConnector; import com.vaadin.client.ui.AbstractLayoutConnector; -import com.vaadin.client.ui.LayoutClickEventHandler; import com.vaadin.client.ui.VFormLayout; import com.vaadin.client.ui.VFormLayout.Caption; import com.vaadin.client.ui.VFormLayout.ErrorFlag; import com.vaadin.client.ui.VFormLayout.VFormLayoutTable; import com.vaadin.shared.ui.Connect; -import com.vaadin.shared.ui.LayoutClickRpc; import com.vaadin.shared.ui.MarginInfo; -import com.vaadin.shared.ui.orderedlayout.AbstractOrderedLayoutServerRpc; import com.vaadin.shared.ui.orderedlayout.AbstractOrderedLayoutState; import com.vaadin.ui.FormLayout; @Connect(FormLayout.class) public class FormLayoutConnector extends AbstractLayoutConnector { - /* - * Handlers & Listeners - */ - - private LayoutClickEventHandler clickEventHandler = new LayoutClickEventHandler( - this) { - - @Override - protected ComponentConnector getChildComponent( - com.google.gwt.user.client.Element element) { - return Util.getConnectorForElement(getConnection(), getWidget(), - element); - } - - @Override - protected LayoutClickRpc getLayoutClickRPC() { - return getRpcProxy(AbstractOrderedLayoutServerRpc.class); - } - }; - @Override public AbstractOrderedLayoutState getState() { return (AbstractOrderedLayoutState) super.getState(); @@ -68,7 +45,6 @@ public class FormLayoutConnector extends AbstractLayoutConnector { public void onStateChanged(StateChangeEvent stateChangeEvent) { super.onStateChanged(stateChangeEvent); - clickEventHandler.handleEventHandlerRegistration(); VFormLayoutTable formLayoutTable = getWidget().table; formLayoutTable.setMargins(new MarginInfo(getState().marginsBitmask)); diff --git a/uitest/src/com/vaadin/tests/components/formlayout/FormLayoutClickListener.java b/uitest/src/com/vaadin/tests/components/formlayout/FormLayoutClickListener.java deleted file mode 100644 index 35b8d9ca86..0000000000 --- a/uitest/src/com/vaadin/tests/components/formlayout/FormLayoutClickListener.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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.formlayout; - -import com.vaadin.event.LayoutEvents.LayoutClickEvent; -import com.vaadin.event.LayoutEvents.LayoutClickListener; -import com.vaadin.server.VaadinRequest; -import com.vaadin.tests.components.AbstractTestUIWithLog; -import com.vaadin.ui.FormLayout; -import com.vaadin.ui.Label; - -/** - * Test UI for Form layout click listener. - * - * @author Vaadin Ltd - */ -public class FormLayoutClickListener extends AbstractTestUIWithLog { - - @Override - protected void setup(VaadinRequest request) { - FormLayout layout = new FormLayout(); - - layout.addStyleName("form"); - - Label label = new Label("target"); - label.addStyleName("label"); - layout.addComponent(label); - - layout.addLayoutClickListener(new LayoutClickListener() { - - @Override - public void layoutClick(LayoutClickEvent event) { - log("Child component: " - + (event.getChildComponent() == null ? null : event - .getChildComponent().getStyleName())); - log("Clicked component: " - + (event.getClickedComponent() == null ? null : event - .getClickedComponent().getStyleName())); - log("Source component: " + event.getComponent().getStyleName()); - } - }); - - addComponent(layout); - } - - @Override - protected String getTestDescription() { - return "LayoutClickListener should work in FormLayout"; - } - - @Override - protected Integer getTicketNumber() { - return 6346; - } - -} diff --git a/uitest/src/com/vaadin/tests/components/formlayout/FormLayoutClickListenerTest.java b/uitest/src/com/vaadin/tests/components/formlayout/FormLayoutClickListenerTest.java deleted file mode 100644 index bcab514a5d..0000000000 --- a/uitest/src/com/vaadin/tests/components/formlayout/FormLayoutClickListenerTest.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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.formlayout; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.openqa.selenium.By; - -import com.vaadin.testbench.elements.FormLayoutElement; -import com.vaadin.tests.tb3.MultiBrowserTest; - -/** - * Test for form layout click listener. - * - * @author Vaadin Ltd - */ -public class FormLayoutClickListenerTest extends MultiBrowserTest { - - @Before - public void setUp() { - openTestURL(); - } - - @Test - public void layoutClickListener_clickOnLayout_childAndClickedComponentsAreNull() { - $(FormLayoutElement.class).first().click(); - - Assert.assertEquals("Source component for click event must be form", - "3. Source component: form", getLogRow(0)); - Assert.assertEquals("Clicked component for click event must be null", - "2. Clicked component: null", getLogRow(1)); - Assert.assertEquals("Child component for click event must be null", - "1. Child component: null", getLogRow(2)); - } - - @Test - public void layoutClickListener_clickOnLabel_lableIsChildAndClickedComponent() { - findElement(By.className("label")).click(); - - Assert.assertEquals("Source component for click event must be form", - "3. Source component: form", getLogRow(0)); - Assert.assertEquals("Clicked component for click event must be label", - "2. Clicked component: label", getLogRow(1)); - Assert.assertEquals("Child component for click event must be label", - "1. Child component: label", getLogRow(2)); - } - -} -- cgit v1.2.3 From 3bc4ff53736ed2a0c3e9e362be3e7de481d1d00f Mon Sep 17 00:00:00 2001 From: Ilya Ermakov Date: Wed, 3 Dec 2014 13:43:15 +0300 Subject: Make SQLContainer.removeAllItems() work properly with paging (#12882) With this patch removeAllItems() works properly when items need to be fetched from database. Change-Id: Ic008ae7c67610cbdde7d71fa071494508678ac8f --- .../vaadin/data/util/sqlcontainer/SQLContainer.java | 1 - .../sqlcontainer/SQLContainerTableQueryTest.java | 20 ++++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/server/src/com/vaadin/data/util/sqlcontainer/SQLContainer.java b/server/src/com/vaadin/data/util/sqlcontainer/SQLContainer.java index 70b392ab80..c0c660c084 100644 --- a/server/src/com/vaadin/data/util/sqlcontainer/SQLContainer.java +++ b/server/src/com/vaadin/data/util/sqlcontainer/SQLContainer.java @@ -652,7 +652,6 @@ public class SQLContainer implements Container, Container.Filterable, if (cachedItems.isEmpty()) { getPage(); } - int size = size(); // this protects against infinite looping int counter = 0; int oldIndex; diff --git a/server/tests/src/com/vaadin/data/util/sqlcontainer/SQLContainerTableQueryTest.java b/server/tests/src/com/vaadin/data/util/sqlcontainer/SQLContainerTableQueryTest.java index 93a27352a5..fbae0ee159 100644 --- a/server/tests/src/com/vaadin/data/util/sqlcontainer/SQLContainerTableQueryTest.java +++ b/server/tests/src/com/vaadin/data/util/sqlcontainer/SQLContainerTableQueryTest.java @@ -1071,6 +1071,26 @@ public class SQLContainerTableQueryTest { Assert.assertEquals(0, container.size()); } + // Set timeout to ensure there is no infinite looping (#12882) + @Test(timeout = 1000) + public void removeAllItems_manyItems_commit_shouldSucceed() + throws SQLException { + SQLContainer container = new SQLContainer(new TableQuery("people", + connectionPool, SQLTestsConstants.sqlGen)); + final int itemNumber = (SQLContainer.CACHE_RATIO + 1) + * SQLContainer.DEFAULT_PAGE_LENGTH + 1; + container.removeAllItems(); + Assert.assertEquals(container.size(), 0); + for (int i = 0; i < itemNumber; ++i) { + container.addItem(); + } + container.commit(); + Assert.assertEquals(container.size(), itemNumber); + Assert.assertTrue(container.removeAllItems()); + container.commit(); + Assert.assertEquals(container.size(), 0); + } + @Test public void commit_tableAddedItem_shouldBeWrittenToDB() throws SQLException { TableQuery query = new TableQuery("people", connectionPool, -- cgit v1.2.3 From 10fa4e4236ed049ef6eac337d23701bc381763ce Mon Sep 17 00:00:00 2001 From: Sauli Tähkäpää Date: Thu, 11 Dec 2014 23:12:12 +0200 Subject: Fix gradient path for notifications in Chameleon. (#15351) Change-Id: Ie1e2726613033ad321e15f7f58e098080d4d9a5f --- .../components/notification/notification.scss | 4 +-- .../themes/chameleon/ChameleonNotification.java | 30 ++++++++++++++++++++++ .../chameleon/ChameleonNotificationTest.java | 25 ++++++++++++++++++ 3 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 uitest/src/com/vaadin/tests/themes/chameleon/ChameleonNotification.java create mode 100644 uitest/src/com/vaadin/tests/themes/chameleon/ChameleonNotificationTest.java diff --git a/WebContent/VAADIN/themes/chameleon/components/notification/notification.scss b/WebContent/VAADIN/themes/chameleon/components/notification/notification.scss index 85fbb3295f..c1a8d119c7 100644 --- a/WebContent/VAADIN/themes/chameleon/components/notification/notification.scss +++ b/WebContent/VAADIN/themes/chameleon/components/notification/notification.scss @@ -9,8 +9,8 @@ div.#{$primaryStyleName} { -moz-box-shadow: 0 2px 5px rgba(0,0,0,.7); box-shadow: 0 2px 5px rgba(0,0,0,.7); //IE8 does not support rgba, using just rgb - background:rgb(255,255,255) url(../img/grad-light-top.png) repeat-x; - background:rgba(255,255,255,.90) url(../img/grad-light-top.png) repeat-x; + background:rgb(255,255,255) url(../../img/grad-light-top.png) repeat-x; + background:rgba(255,255,255,.90) url(../../img/grad-light-top.png) repeat-x; } .#{$primaryStyleName} p { diff --git a/uitest/src/com/vaadin/tests/themes/chameleon/ChameleonNotification.java b/uitest/src/com/vaadin/tests/themes/chameleon/ChameleonNotification.java new file mode 100644 index 0000000000..efb953530c --- /dev/null +++ b/uitest/src/com/vaadin/tests/themes/chameleon/ChameleonNotification.java @@ -0,0 +1,30 @@ +package com.vaadin.tests.themes.chameleon; + +import com.vaadin.annotations.Theme; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Button; +import com.vaadin.ui.Notification; +import com.vaadin.ui.themes.ChameleonTheme; + +@Theme(ChameleonTheme.THEME_NAME) +public class ChameleonNotification extends AbstractTestUI { + @Override + protected void setup(VaadinRequest request) { + + + addButton("Notification", new Button.ClickListener() { + @Override + public void buttonClick(Button.ClickEvent event) { + Notification notification = new Notification("Notification"); + notification.setDelayMsec(30000); + notification.show(getUI().getPage()); + } + }); + } + + @Override + protected Integer getTicketNumber() { + return 15351; + } +} diff --git a/uitest/src/com/vaadin/tests/themes/chameleon/ChameleonNotificationTest.java b/uitest/src/com/vaadin/tests/themes/chameleon/ChameleonNotificationTest.java new file mode 100644 index 0000000000..ef41ef3df9 --- /dev/null +++ b/uitest/src/com/vaadin/tests/themes/chameleon/ChameleonNotificationTest.java @@ -0,0 +1,25 @@ +package com.vaadin.tests.themes.chameleon; + +import com.vaadin.testbench.elements.ButtonElement; +import com.vaadin.testbench.elements.NotificationElement; +import com.vaadin.tests.tb3.MultiBrowserTest; +import org.junit.Test; + +import java.io.IOException; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThat; + +public class ChameleonNotificationTest extends MultiBrowserTest { + @Test + public void gradientPathIsCorrect() throws IOException { + openTestURL(); + $(ButtonElement.class).first().click(); + + NotificationElement notificationElement + = $(NotificationElement.class).first(); + + assertThat(notificationElement.getCssValue("background-image"), + containsString("chameleon/img/grad")); + } +} \ No newline at end of file -- cgit v1.2.3