Browse Source

Implement error level on client side (#9817)

Add additional class names and style to components and error indicators to distinguish different error levels.

Vaadin 8 implementation of #9816. Cherry picked changes and added compatibility package component changes and tests.

Resolves #3139
tags/8.2.0.alpha2
Adam Wagner 6 years ago
parent
commit
697f770287
55 changed files with 1409 additions and 256 deletions
  1. 2
    0
      all/src/main/templates/release-notes.html
  2. 5
    0
      client/src/main/java/com/vaadin/client/StyleConstants.java
  3. 170
    4
      client/src/main/java/com/vaadin/client/TooltipInfo.java
  4. 45
    29
      client/src/main/java/com/vaadin/client/VCaption.java
  5. 14
    0
      client/src/main/java/com/vaadin/client/VErrorMessage.java
  6. 2
    0
      client/src/main/java/com/vaadin/client/VTooltip.java
  7. 54
    9
      client/src/main/java/com/vaadin/client/WidgetUtil.java
  8. 24
    1
      client/src/main/java/com/vaadin/client/ui/AbstractComponentConnector.java
  9. 42
    0
      client/src/main/java/com/vaadin/client/ui/HasErrorIndicatorElement.java
  10. 3
    1
      client/src/main/java/com/vaadin/client/ui/VAccordion.java
  11. 21
    2
      client/src/main/java/com/vaadin/client/ui/VButton.java
  12. 24
    2
      client/src/main/java/com/vaadin/client/ui/VCheckBox.java
  13. 33
    17
      client/src/main/java/com/vaadin/client/ui/VFormLayout.java
  14. 21
    2
      client/src/main/java/com/vaadin/client/ui/VLink.java
  15. 22
    2
      client/src/main/java/com/vaadin/client/ui/VNativeButton.java
  16. 23
    19
      client/src/main/java/com/vaadin/client/ui/VPanel.java
  17. 3
    1
      client/src/main/java/com/vaadin/client/ui/VTabsheet.java
  18. 2
    16
      client/src/main/java/com/vaadin/client/ui/button/ButtonConnector.java
  19. 3
    21
      client/src/main/java/com/vaadin/client/ui/checkbox/CheckBoxConnector.java
  20. 2
    1
      client/src/main/java/com/vaadin/client/ui/formlayout/FormLayoutConnector.java
  21. 2
    14
      client/src/main/java/com/vaadin/client/ui/link/LinkConnector.java
  22. 2
    17
      client/src/main/java/com/vaadin/client/ui/nativebutton/NativeButtonConnector.java
  23. 3
    2
      client/src/main/java/com/vaadin/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java
  24. 58
    9
      client/src/main/java/com/vaadin/client/ui/orderedlayout/Slot.java
  25. 0
    2
      client/src/main/java/com/vaadin/client/ui/panel/PanelConnector.java
  26. 8
    1
      compatibility-client/src/main/java/com/vaadin/v7/client/ui/checkbox/CheckBoxConnector.java
  27. 1
    0
      compatibility-client/src/main/java/com/vaadin/v7/client/ui/form/FormConnector.java
  28. 1
    1
      compatibility-server/src/main/java/com/vaadin/v7/data/Buffered.java
  29. 1
    1
      compatibility-server/src/main/java/com/vaadin/v7/data/Validator.java
  30. 1
    0
      compatibility-server/src/main/java/com/vaadin/v7/ui/Grid.java
  31. 1
    1
      compatibility-server/src/test/java/com/vaadin/v7/ui/AbstractLegacyComponentDeclarativeTest.java
  32. 2
    0
      server/src/main/java/com/vaadin/server/AbstractErrorMessage.java
  33. 4
    2
      server/src/main/java/com/vaadin/server/CompositeErrorMessage.java
  34. 6
    59
      server/src/main/java/com/vaadin/server/ErrorMessage.java
  35. 3
    1
      server/src/main/java/com/vaadin/server/SystemError.java
  36. 2
    0
      server/src/main/java/com/vaadin/server/UserError.java
  37. 3
    1
      server/src/main/java/com/vaadin/ui/AbstractComponent.java
  38. 9
    3
      server/src/main/java/com/vaadin/ui/TabSheet.java
  39. 1
    1
      server/src/test/java/com/vaadin/tests/server/component/abstractcomponent/AbstractComponentDeclarativeTest.java
  40. 1
    1
      server/src/test/java/com/vaadin/tests/server/component/abstractcomponent/AbstractComponentDeclarativeTestBase.java
  41. 10
    3
      shared/src/main/java/com/vaadin/shared/AbstractComponentState.java
  42. 58
    0
      shared/src/main/java/com/vaadin/shared/ui/ErrorLevel.java
  43. 9
    0
      shared/src/main/java/com/vaadin/shared/ui/tabsheet/TabState.java
  44. 1
    1
      tests/screenshots
  45. 55
    0
      themes/src/main/themes/VAADIN/themes/valo/components/_combobox.scss
  46. 55
    0
      themes/src/main/themes/VAADIN/themes/valo/components/_datefield.scss
  47. 67
    3
      themes/src/main/themes/VAADIN/themes/valo/components/_textfield.scss
  48. 34
    0
      themes/src/main/themes/VAADIN/themes/valo/components/_twincolselect.scss
  49. 22
    2
      themes/src/main/themes/VAADIN/themes/valo/shared/_global.scss
  50. 60
    0
      themes/src/main/themes/VAADIN/themes/valo/shared/_tooltip.scss
  51. 41
    1
      themes/src/main/themes/VAADIN/themes/valo/shared/_variables.scss
  52. 210
    0
      uitest/src/main/java/com/vaadin/tests/components/ErrorLevels.java
  53. 1
    1
      uitest/src/main/java/com/vaadin/tests/themes/valo/CommonParts.java
  54. 160
    0
      uitest/src/test/java/com/vaadin/tests/components/ErrorLevelsTest.java
  55. 2
    2
      uitest/src/test/java/com/vaadin/tests/components/tabsheet/TabSheetErrorTooltipTest.java

+ 2
- 0
all/src/main/templates/release-notes.html View File

@@ -106,6 +106,8 @@
<li><tt>DataCommunicator</tt> method <tt>getDataProviderSize</tt> is now <tt>public</tt>, not <tt>protected</tt>.</li>
<li><tt>Binder</tt> method <tt>getBindings</tt> now returns a Collection, not a Set.</li>
<li><tt>BindingBuilder</tt> now works like a proper builder. Adding a converter will not mark Binding as <tt>bound</tt> allowing chaining to the same object.</li>
<li><tt>ErrorLevel</tt> is removed from <tt>ErrorMessage</tt> and now <tt>com.vaadin.shared.ui.ErrorLevel</tt> should be used.</li>
<li>Error indicators are now <tt>&lt;span class="v-errorindicator"&gt;&lt;/span&gt;</tt> elements.</li>

<h2>For incompatible or behaviour-altering changes in 8.1, please see <a href="https://vaadin.com/download/release/8.1/8.1.0/release-notes.html#incompatible">8.1 release notes</a></h2>

+ 5
- 0
client/src/main/java/com/vaadin/client/StyleConstants.java View File

@@ -44,4 +44,9 @@ public class StyleConstants {
public static final String REQUIRED_EXT = "-required";

public static final String ERROR_EXT = "-error";

/**
* Style name and style name prefix for the error indicator element.
*/
public static final String STYLE_NAME_ERROR_INDICATOR = "v-errorindicator";
}

+ 170
- 4
client/src/main/java/com/vaadin/client/TooltipInfo.java View File

@@ -16,8 +16,13 @@
package com.vaadin.client;

import com.vaadin.shared.ui.ContentMode;
import com.vaadin.shared.ui.ErrorLevel;
import com.vaadin.shared.util.SharedUtil;

/**
* An object that contains information about a tooltip, such as the tooltip's
* title, error message, error level and an ID.
*/
public class TooltipInfo {

private String title;
@@ -26,14 +31,25 @@ public class TooltipInfo {

private String errorMessageHtml;

private ErrorLevel errorLevel;

// Contains the tooltip's identifier. If a tooltip's contents and this
// identifier haven't changed, the tooltip won't be updated in subsequent
// events.
private Object identifier;

/**
* Constructs a new tooltip info instance.
*/
public TooltipInfo() {
}

/**
* Constructs a new tooltip info instance.
*
* @param tooltip
* tooltip title
*/
public TooltipInfo(String tooltip) {
this(tooltip, ContentMode.PREFORMATTED);
}
@@ -41,12 +57,12 @@ public class TooltipInfo {
/**
* Constructs a new instance using the {@code tooltip} for the title and
* {@code errorMessage} as a description.
*
*
* @param tooltip
* tooltip title
* @param errorMessage
* error description
*
*
* @deprecated use {@link #TooltipInfo(String, ContentMode, String)} instead
*/
@Deprecated
@@ -57,13 +73,14 @@ public class TooltipInfo {
/**
* Constructs a new instance using the {@code tooltip} for the title,
* {@code errorMessage} as a description and {@code identifier} as its id.
*
*
* @param tooltip
* tooltip title
* @param errorMessage
* error description
* @param identifier
*
* the tooltip's identifier
*
* @deprecated use {@link #TooltipInfo(String, ContentMode, String, Object)}
* instead
*/
@@ -72,55 +89,194 @@ public class TooltipInfo {
this(tooltip, ContentMode.HTML, errorMessage, identifier);
}

/**
* Constructs a new instance using the {@code tooltip} for the title,
* {@code errorMessage} as a description, {@code identifier} as its id and
* {@code errorLevel} as the error level.
*
* @param tooltip
* tooltip title
* @param errorMessage
* error description
* @param identifier
* the tooltip's identifier
* @param errorLevel
* error level
*
* @deprecated use {@link #TooltipInfo(String, ContentMode, String, Object,
* ErrorLevel)} instead
* @since 8.2
*/
@Deprecated
public TooltipInfo(String tooltip, String errorMessage, Object identifier,
ErrorLevel errorLevel) {
this(tooltip, ContentMode.HTML, errorMessage, identifier, errorLevel);
}

/**
* Constructs a new tooltip info instance.
*
* @param tooltip
* tooltip title
* @param mode
* content mode
*/
public TooltipInfo(String tooltip, ContentMode mode) {
setTitle(tooltip);
setContentMode(mode);
}

/**
* Constructs a new tooltip info instance.
*
* @param tooltip
* tooltip title
* @param mode
* content mode
* @param errorMessage
* error message
*/
public TooltipInfo(String tooltip, ContentMode mode, String errorMessage) {
this(tooltip, mode, errorMessage, null);
}

/**
* Constructs a new tooltip info instance.
*
* @param tooltip
* tooltip title
* @param mode
* content mode
* @param errorMessage
* error message
* @param identifier
* the tooltip's identifier
*/
public TooltipInfo(String tooltip, ContentMode mode, String errorMessage,
Object identifier) {
this(tooltip, mode, errorMessage, identifier, null);
}

/**
* Constructs a new tooltip info instance.
*
* @param tooltip
* tooltip title
* @param mode
* content mode
* @param errorMessage
* error message
* @param identifier
* the tooltip's identifier
* @param errorLevel
* error level
*/
public TooltipInfo(String tooltip, ContentMode mode, String errorMessage,
Object identifier, ErrorLevel errorLevel) {
setIdentifier(identifier);
setTitle(tooltip);
setContentMode(mode);
setErrorMessage(errorMessage);
setErrorLevel(errorLevel);
}

/**
* Sets the tooltip's identifier.
*
* @param identifier
* the identifier to set
*/
public void setIdentifier(Object identifier) {
this.identifier = identifier;
}

/**
* Gets the tooltip's identifier.
*
* @return the identifier
*/
public Object getIdentifier() {
return identifier;
}

/**
* Gets the tooltip title.
*
* @return the title
*/
public String getTitle() {
return title;
}

/**
* Sets the tooltip title.
*
* @param title
* the title to set
*/
public void setTitle(String title) {
this.title = title;
}

/**
* Gets the error message.
*
* @return the error message
*/
public String getErrorMessage() {
return errorMessageHtml;
}

/**
* Sets the error message.
*
* @param errorMessage
* the error message to set
*/
public void setErrorMessage(String errorMessage) {
errorMessageHtml = errorMessage;
}

/**
* Gets the tooltip title's content mode.
*
* @return the content mode
*/
public ContentMode getContentMode() {
return contentMode;
}

/**
* Sets the tooltip title's content mode.
*
* @param contentMode
* the content mode to set
*/
public void setContentMode(ContentMode contentMode) {
this.contentMode = contentMode;
}

/**
* Gets the error level.
*
* @return the error level
* @since
*/
public ErrorLevel getErrorLevel() {
return errorLevel;
}

/**
* Sets the error level.
*
* @param errorLevel
* the error level to set
* @since
*/
public void setErrorLevel(ErrorLevel errorLevel) {
this.errorLevel = errorLevel;
}

/**
* Checks is a message has been defined for the tooltip.
*
@@ -132,9 +288,19 @@ public class TooltipInfo {
|| (errorMessageHtml != null && !errorMessageHtml.isEmpty());
}

/**
* Indicates whether another tooltip info instance is equal to this one. Two
* instances are equal if their title, error message, error level and
* identifier are equal.
*
* @param other
* the reference tooltip info instance with which to compare
* @return {@code true} if the instances are equal, {@code false} otherwise
*/
public boolean equals(TooltipInfo other) {
return (other != null && SharedUtil.equals(other.title, title)
&& SharedUtil.equals(other.errorMessageHtml, errorMessageHtml)
&& SharedUtil.equals(other.errorLevel, errorLevel)
&& other.identifier == identifier);
}
}

+ 45
- 29
client/src/main/java/com/vaadin/client/VCaption.java View File

@@ -25,8 +25,10 @@ import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasHTML;
import com.vaadin.client.WidgetUtil.ErrorUtil;
import com.vaadin.client.communication.StateChangeEvent;
import com.vaadin.client.ui.HasErrorIndicator;
import com.vaadin.client.ui.HasErrorIndicatorElement;
import com.vaadin.client.ui.HasRequiredIndicator;
import com.vaadin.client.ui.Icon;
import com.vaadin.client.ui.ImageIcon;
@@ -34,8 +36,9 @@ import com.vaadin.client.ui.aria.AriaHelper;
import com.vaadin.shared.AbstractComponentState;
import com.vaadin.shared.ComponentConstants;
import com.vaadin.shared.ui.ComponentStateUtil;
import com.vaadin.shared.ui.ErrorLevel;

public class VCaption extends HTML {
public class VCaption extends HTML implements HasErrorIndicatorElement {

public static final String CLASSNAME = "v-caption";

@@ -259,23 +262,17 @@ public class VCaption extends HTML {
AriaHelper.handleInputInvalid(owner.getWidget(), showError);

if (showError) {
if (errorIndicatorElement == null) {
errorIndicatorElement = DOM.createDiv();
DOM.setInnerHTML(errorIndicatorElement, "&nbsp;");
DOM.setElementProperty(errorIndicatorElement, "className",
"v-errorindicator");
setErrorIndicatorElementVisible(true);

DOM.insertChild(getElement(), errorIndicatorElement,
getInsertPosition(InsertPosition.ERROR));
// Hide error indicator from assistive devices
Roles.getTextboxRole()
.setAriaHiddenState(errorIndicatorElement, true);

// Hide error indicator from assistive devices
Roles.getTextboxRole().setAriaHiddenState(errorIndicatorElement,
true);
}
} else if (errorIndicatorElement != null) {
// Remove existing
getElement().removeChild(errorIndicatorElement);
errorIndicatorElement = null;
ErrorUtil.setErrorLevelStyle(errorIndicatorElement,
StyleConstants.STYLE_NAME_ERROR_INDICATOR,
owner.getState().errorLevel);
} else {
setErrorIndicatorElementVisible(false);
}

return (wasPlacedAfterComponent != placedAfterComponent);
@@ -322,6 +319,14 @@ public class VCaption extends HTML {
public boolean updateCaptionWithoutOwner(String caption, boolean disabled,
boolean hasDescription, boolean hasError, String iconURL,
String iconAltText) {
return updateCaptionWithoutOwner(caption, disabled, hasDescription,
hasError, null, iconURL, iconAltText);
}

@Deprecated
public boolean updateCaptionWithoutOwner(String caption, boolean disabled,
boolean hasDescription, boolean hasError, ErrorLevel errorLevel,
String iconURL, String iconAltText) {
boolean wasPlacedAfterComponent = placedAfterComponent;

// Caption is placed after component unless there is some part which
@@ -401,19 +406,11 @@ public class VCaption extends HTML {
}

if (hasError) {
if (errorIndicatorElement == null) {
errorIndicatorElement = DOM.createDiv();
DOM.setInnerHTML(errorIndicatorElement, "&nbsp;");
DOM.setElementProperty(errorIndicatorElement, "className",
"v-errorindicator");

DOM.insertChild(getElement(), errorIndicatorElement,
getInsertPosition(InsertPosition.ERROR));
}
} else if (errorIndicatorElement != null) {
// Remove existing
getElement().removeChild(errorIndicatorElement);
errorIndicatorElement = null;
setErrorIndicatorElementVisible(true);
ErrorUtil.setErrorLevelStyle(errorIndicatorElement,
StyleConstants.STYLE_NAME_ERROR_INDICATOR, errorLevel);
} else {
setErrorIndicatorElementVisible(false);
}

return (wasPlacedAfterComponent != placedAfterComponent);
@@ -775,4 +772,23 @@ public class VCaption extends HTML {
private static Logger getLogger() {
return Logger.getLogger(VCaption.class.getName());
}

@Override
public Element getErrorIndicatorElement() {
return errorIndicatorElement;
}

@Override
public void setErrorIndicatorElementVisible(boolean visible) {
if (visible) {
if (errorIndicatorElement == null) {
errorIndicatorElement = ErrorUtil.createErrorIndicatorElement();
DOM.insertChild(getElement(), errorIndicatorElement,
getInsertPosition(InsertPosition.ERROR));
}
} else if (errorIndicatorElement != null) {
getElement().removeChild(errorIndicatorElement);
errorIndicatorElement = null;
}
}
}

+ 14
- 0
client/src/main/java/com/vaadin/client/VErrorMessage.java View File

@@ -22,6 +22,8 @@ import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ui.VOverlay;
import com.vaadin.client.WidgetUtil.ErrorUtil;
import com.vaadin.shared.ui.ErrorLevel;

public class VErrorMessage extends FlowPanel {
public static final String CLASSNAME = "v-errormessage";
@@ -57,6 +59,18 @@ public class VErrorMessage extends FlowPanel {
}
}

/**
* Sets the correct error level style name for the error message and removes
* all previous style names.
*
* @param errorLevel
* error level
* @since
*/
public void updateErrorLevel(ErrorLevel errorLevel) {
ErrorUtil.setErrorLevelStyle(getStyleElement(), CLASSNAME, errorLevel);
}

/**
* Shows this error message next to given element.
*

+ 2
- 0
client/src/main/java/com/vaadin/client/VTooltip.java View File

@@ -138,6 +138,7 @@ public class VTooltip extends VOverlay {
&& !info.getErrorMessage().isEmpty()) {
em.setVisible(true);
em.updateMessage(info.getErrorMessage());
em.updateErrorLevel(info.getErrorLevel());
} else {
em.setVisible(false);
}
@@ -459,6 +460,7 @@ public class VTooltip extends VOverlay {
@Override
public void hide() {
em.updateMessage("");
em.updateErrorLevel(null);
description.setHTML("");

updatePosition(null, true);

+ 54
- 9
client/src/main/java/com/vaadin/client/WidgetUtil.java View File

@@ -44,6 +44,7 @@ import com.google.gwt.user.client.EventListener;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.shared.ui.ErrorLevel;
import com.vaadin.shared.util.SharedUtil;

/**
@@ -790,7 +791,7 @@ public class WidgetUtil {
com.google.gwt.dom.client.Element el, String p)
/*-{
try {
if (el.currentStyle) {
// IE
return el.currentStyle[p];
@@ -805,7 +806,7 @@ public class WidgetUtil {
} catch (e) {
return "";
}
}-*/;

/**
@@ -819,7 +820,7 @@ public class WidgetUtil {
try {
el.focus();
} catch (e) {
}
}-*/;

@@ -1172,7 +1173,7 @@ public class WidgetUtil {
if ($wnd.document.activeElement) {
return $wnd.document.activeElement;
}
return null;
}-*/;

@@ -1243,11 +1244,11 @@ public class WidgetUtil {
/*-{
var top = elem.offsetTop;
var height = elem.offsetHeight;
if (elem.parentNode != elem.offsetParent) {
top -= elem.parentNode.offsetTop;
}
var cur = elem.parentNode;
while (cur && (cur.nodeType == 1)) {
if (top < cur.scrollTop) {
@@ -1256,12 +1257,12 @@ public class WidgetUtil {
if (top + height > cur.scrollTop + cur.clientHeight) {
cur.scrollTop = (top + height) - cur.clientHeight;
}
var offsetTop = cur.offsetTop;
if (cur.parentNode != cur.offsetParent) {
offsetTop -= cur.parentNode.offsetTop;
}
top += offsetTop - cur.scrollTop;
cur = cur.parentNode;
}
@@ -1710,7 +1711,7 @@ public class WidgetUtil {
}
var heightWithoutBorder = cloneElement.offsetHeight;
parentElement.removeChild(cloneElement);
return heightWithBorder - heightWithoutBorder;
}
}-*/;
@@ -1866,4 +1867,48 @@ public class WidgetUtil {
int relativeTop = element.getAbsoluteTop() - Window.getScrollTop();
return WidgetUtil.getTouchOrMouseClientY(event) - relativeTop;
}

/**
* Utility methods for displaying error message on components.
*
* @since 8.2
*/
public static class ErrorUtil {

/**
* Sets the error level style name for the given element and removes all
* previously applied error level style names. The style name has the
* {@code prefix-errorLevel} format.
*
* @param element
* element to apply the style name to
* @param prefix
* part of the style name before the error level string
* @param errorLevel
* error level for which the style will be applied
*/
public static void setErrorLevelStyle(Element element, String prefix,
ErrorLevel errorLevel) {
for (ErrorLevel errorLevelValue : ErrorLevel.values()) {
String className =
prefix + "-" + errorLevelValue.toString().toLowerCase();
if (errorLevel == errorLevelValue) {
element.addClassName(className);
} else {
element.removeClassName(className);
}
}
}

/**
* Creates an element to use by widgets as an error indicator.
*
* @return the error indicator element
*/
public static Element createErrorIndicatorElement() {
Element indicator = DOM.createSpan();
indicator.setClassName(StyleConstants.STYLE_NAME_ERROR_INDICATOR);
return indicator;
}
}
}

+ 24
- 1
client/src/main/java/com/vaadin/client/ui/AbstractComponentConnector.java View File

@@ -46,6 +46,7 @@ import com.vaadin.client.UIDL;
import com.vaadin.client.Util;
import com.vaadin.client.VConsole;
import com.vaadin.client.WidgetUtil;
import com.vaadin.client.WidgetUtil.ErrorUtil;
import com.vaadin.client.annotations.OnStateChange;
import com.vaadin.client.communication.StateChangeEvent;
import com.vaadin.client.extensions.DragSourceExtensionConnector;
@@ -489,6 +490,27 @@ public abstract class AbstractComponentConnector extends AbstractConnector
Profiler.leave("AbstractComponentConnector.onStateChanged");
}

@OnStateChange({"errorMessage", "errorLevel"})
private void setErrorLevel() {
// Add or remove the widget's error level style name
ErrorUtil.setErrorLevelStyle(getWidget().getElement(),
getWidget().getStylePrimaryName() + StyleConstants.ERROR_EXT,
getState().errorLevel);

// Add or remove error indicator element
if (getWidget() instanceof HasErrorIndicatorElement) {
HasErrorIndicatorElement widget = (HasErrorIndicatorElement) getWidget();
if (getState().errorMessage != null) {
widget.setErrorIndicatorElementVisible(true);
ErrorUtil.setErrorLevelStyle(widget.getErrorIndicatorElement(),
StyleConstants.STYLE_NAME_ERROR_INDICATOR,
getState().errorLevel);
} else {
widget.setErrorIndicatorElementVisible(false);
}
}
}

@Override
public void setWidgetEnabled(boolean widgetEnabled) {
// add or remove v-disabled style name from the widget
@@ -764,7 +786,8 @@ public abstract class AbstractComponentConnector extends AbstractConnector
@Override
public TooltipInfo getTooltipInfo(Element element) {
return new TooltipInfo(getState().description,
getState().descriptionContentMode, getState().errorMessage);
getState().descriptionContentMode, getState().errorMessage,
null, getState().errorLevel);
}

@Override

+ 42
- 0
client/src/main/java/com/vaadin/client/ui/HasErrorIndicatorElement.java View File

@@ -0,0 +1,42 @@
/*
* Copyright 2000-2016 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.client.ui;

import com.google.gwt.dom.client.Element;

/**
* Implemented by widgets supporting an error indicator.
*
* @since 8.2
*/
public interface HasErrorIndicatorElement {

/**
* Gets the error indicator element.
*
* @return the error indicator element
*/
Element getErrorIndicatorElement();

/**
* Sets the visibility of the error indicator element.
*
* @param visible
* {@code true} to show the error indicator element, {@code false}
* to hide it
*/
void setErrorIndicatorElementVisible(boolean visible);
}

+ 3
- 1
client/src/main/java/com/vaadin/client/ui/VAccordion.java View File

@@ -364,8 +364,10 @@ public class VAccordion extends VTabsheetBase {
caption.updateCaptionWithoutOwner(tabState.caption,
!tabState.enabled, hasAttribute(tabState.description),
hasAttribute(tabState.componentError),
tabState.componentErrorLevel,
connector.getResourceUrl(
ComponentConstants.ICON_RESOURCE + tabState.key));
ComponentConstants.ICON_RESOURCE + tabState.key),
tabState.iconAltText);
}

private boolean hasAttribute(String string) {

+ 21
- 2
client/src/main/java/com/vaadin/client/ui/VButton.java View File

@@ -30,8 +30,10 @@ import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.BrowserInfo;
import com.vaadin.client.Util;
import com.vaadin.client.WidgetUtil;
import com.vaadin.client.WidgetUtil.ErrorUtil;

public class VButton extends FocusWidget implements ClickHandler {
public class VButton extends FocusWidget implements ClickHandler,
HasErrorIndicatorElement {

public static final String CLASSNAME = "v-button";
private static final String CLASSNAME_PRESSED = "v-pressed";
@@ -48,7 +50,7 @@ public class VButton extends FocusWidget implements ClickHandler {
public final Element wrapper = DOM.createSpan();

/** For internal use only. May be removed or replaced in the future. */
public Element errorIndicatorElement;
private Element errorIndicatorElement;

/** For internal use only. May be removed or replaced in the future. */
public final Element captionElement = DOM.createSpan();
@@ -481,4 +483,21 @@ public class VButton extends FocusWidget implements ClickHandler {
return ret;
}-*/;

@Override
public Element getErrorIndicatorElement() {
return errorIndicatorElement;
}

@Override
public void setErrorIndicatorElementVisible(boolean visible) {
if (visible) {
if (errorIndicatorElement == null) {
errorIndicatorElement = ErrorUtil.createErrorIndicatorElement();
wrapper.insertFirst(errorIndicatorElement);
}
} else if (errorIndicatorElement != null) {
wrapper.removeChild(errorIndicatorElement);
errorIndicatorElement = null;
}
}
}

+ 24
- 2
client/src/main/java/com/vaadin/client/ui/VCheckBox.java View File

@@ -25,12 +25,14 @@ import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.BrowserInfo;
import com.vaadin.client.Util;
import com.vaadin.client.VTooltip;
import com.vaadin.client.WidgetUtil.ErrorUtil;
import com.vaadin.client.ui.aria.AriaHelper;
import com.vaadin.client.ui.aria.HandlesAriaInvalid;
import com.vaadin.client.ui.aria.HandlesAriaRequired;

public class VCheckBox extends com.google.gwt.user.client.ui.CheckBox
implements Field, HandlesAriaInvalid, HandlesAriaRequired {
implements Field, HandlesAriaInvalid, HandlesAriaRequired,
HasErrorIndicatorElement {

public static final String CLASSNAME = "v-checkbox";

@@ -41,7 +43,7 @@ public class VCheckBox extends com.google.gwt.user.client.ui.CheckBox
public ApplicationConnection client;

/** For internal use only. May be removed or replaced in the future. */
public Element errorIndicatorElement;
private Element errorIndicatorElement;

/** For internal use only. May be removed or replaced in the future. */
public Icon icon;
@@ -101,4 +103,24 @@ public class VCheckBox extends com.google.gwt.user.client.ui.CheckBox
public void setAriaInvalid(boolean invalid) {
AriaHelper.handleInputInvalid(getCheckBoxElement(), invalid);
}

@Override
public Element getErrorIndicatorElement() {
return errorIndicatorElement;
}

@Override
public void setErrorIndicatorElementVisible(boolean visible) {
if (visible) {
if (errorIndicatorElement == null) {
errorIndicatorElement = ErrorUtil.createErrorIndicatorElement();
getElement().appendChild(errorIndicatorElement);
DOM.sinkEvents(errorIndicatorElement,
VTooltip.TOOLTIP_EVENTS | Event.ONCLICK);
}
} else if (errorIndicatorElement != null) {
getElement().removeChild(errorIndicatorElement);
errorIndicatorElement = null;
}
}
}

+ 33
- 17
client/src/main/java/com/vaadin/client/ui/VFormLayout.java View File

@@ -34,10 +34,12 @@ import com.vaadin.client.ComponentConnector;
import com.vaadin.client.Focusable;
import com.vaadin.client.StyleConstants;
import com.vaadin.client.VTooltip;
import com.vaadin.client.WidgetUtil.ErrorUtil;
import com.vaadin.client.ui.aria.AriaHelper;
import com.vaadin.shared.AbstractComponentState;
import com.vaadin.shared.ComponentConstants;
import com.vaadin.shared.ui.ComponentStateUtil;
import com.vaadin.shared.ui.ErrorLevel;
import com.vaadin.shared.ui.MarginInfo;

/**
@@ -201,10 +203,10 @@ public class VFormLayout extends SimplePanel {
}

public void updateError(Widget widget, String errorMessage,
boolean hideErrors) {
ErrorLevel errorLevel, boolean hideErrors) {
final ErrorFlag e = widgetToError.get(widget);
if (e != null) {
e.updateError(errorMessage, hideErrors);
e.updateError(errorMessage, errorLevel, hideErrors);
}

}
@@ -340,7 +342,7 @@ public class VFormLayout extends SimplePanel {
}

/** For internal use only. May be removed or replaced in the future. */
public class ErrorFlag extends HTML {
public class ErrorFlag extends HTML implements HasErrorIndicatorElement {
private static final String CLASSNAME = VFormLayout.CLASSNAME
+ "-error-indicator";
Element errorIndicatorElement;
@@ -361,7 +363,8 @@ public class VFormLayout extends SimplePanel {
return owner;
}

public void updateError(String errorMessage, boolean hideErrors) {
public void updateError(String errorMessage, ErrorLevel errorLevel,
boolean hideErrors) {
boolean showError = null != errorMessage;
if (hideErrors) {
showError = false;
@@ -370,24 +373,37 @@ public class VFormLayout extends SimplePanel {
AriaHelper.handleInputInvalid(owner.getWidget(), showError);

if (showError) {
if (errorIndicatorElement == null) {
errorIndicatorElement = DOM.createDiv();
DOM.setInnerHTML(errorIndicatorElement, "&nbsp;");
DOM.setElementProperty(errorIndicatorElement, "className",
"v-errorindicator");
DOM.appendChild(getElement(), errorIndicatorElement);
setErrorIndicatorElementVisible(true);

// Hide the error indicator from screen reader, as this
// information is set directly at the input field
Roles.getFormRole()
.setAriaHiddenState(errorIndicatorElement, true);
}
// Hide the error indicator from screen reader, as this
// information is set directly at the input field
Roles.getFormRole()
.setAriaHiddenState(errorIndicatorElement, true);

ErrorUtil.setErrorLevelStyle(errorIndicatorElement,
StyleConstants.STYLE_NAME_ERROR_INDICATOR, errorLevel);
} else {
setErrorIndicatorElementVisible(false);
}
}

@Override
public Element getErrorIndicatorElement() {
return errorIndicatorElement;
}

@Override
public void setErrorIndicatorElementVisible(boolean visible) {
if (visible) {
if (errorIndicatorElement == null) {
errorIndicatorElement = ErrorUtil
.createErrorIndicatorElement();
getElement().appendChild(errorIndicatorElement);
}
} else if (errorIndicatorElement != null) {
DOM.removeChild(getElement(), errorIndicatorElement);
getElement().removeChild(errorIndicatorElement);
errorIndicatorElement = null;
}
}

}
}

+ 21
- 2
client/src/main/java/com/vaadin/client/ui/VLink.java View File

@@ -25,9 +25,11 @@ import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasEnabled;
import com.vaadin.client.Util;
import com.vaadin.client.WidgetUtil.ErrorUtil;
import com.vaadin.shared.ui.BorderStyle;

public class VLink extends HTML implements ClickHandler, HasEnabled {
public class VLink extends HTML implements ClickHandler, HasEnabled,
HasErrorIndicatorElement {

public static final String CLASSNAME = "v-link";

@@ -59,7 +61,7 @@ public class VLink extends HTML implements ClickHandler, HasEnabled {
public int targetHeight;

/** For internal use only. May be removed or replaced in the future. */
public Element errorIndicatorElement;
private Element errorIndicatorElement;

/** For internal use only. May be removed or replaced in the future. */
public final Element anchor = DOM.createAnchor();
@@ -145,4 +147,21 @@ public class VLink extends HTML implements ClickHandler, HasEnabled {
this.enabled = enabled;
}

@Override
public Element getErrorIndicatorElement() {
return errorIndicatorElement;
}

@Override
public void setErrorIndicatorElementVisible(boolean visible) {
if (visible) {
if (errorIndicatorElement == null) {
errorIndicatorElement = ErrorUtil.createErrorIndicatorElement();
getElement().insertFirst(errorIndicatorElement);
}
} else if (errorIndicatorElement != null) {
getElement().removeChild(errorIndicatorElement);
errorIndicatorElement = null;
}
}
}

+ 22
- 2
client/src/main/java/com/vaadin/client/ui/VNativeButton.java View File

@@ -27,10 +27,12 @@ import com.vaadin.client.BrowserInfo;
import com.vaadin.client.MouseEventDetailsBuilder;
import com.vaadin.client.StyleConstants;
import com.vaadin.client.Util;
import com.vaadin.client.WidgetUtil.ErrorUtil;
import com.vaadin.shared.MouseEventDetails;
import com.vaadin.shared.ui.button.ButtonServerRpc;

public class VNativeButton extends Button implements ClickHandler {
public class VNativeButton extends Button implements ClickHandler,
HasErrorIndicatorElement {

public static final String CLASSNAME = "v-nativebutton";

@@ -44,7 +46,7 @@ public class VNativeButton extends Button implements ClickHandler {
public ButtonServerRpc buttonRpcProxy;

/** For internal use only. May be removed or replaced in the future. */
public Element errorIndicatorElement;
private Element errorIndicatorElement;

/** For internal use only. May be removed or replaced in the future. */
public final Element captionElement = DOM.createSpan();
@@ -159,4 +161,22 @@ public class VNativeButton extends Button implements ClickHandler {
clickPending = false;
}

@Override
public Element getErrorIndicatorElement() {
return errorIndicatorElement;
}

@Override
public void setErrorIndicatorElementVisible(boolean visible) {
if (visible) {
if (errorIndicatorElement == null) {
errorIndicatorElement = ErrorUtil.createErrorIndicatorElement();
getElement()
.insertBefore(errorIndicatorElement, captionElement);
}
} else if (errorIndicatorElement != null) {
getElement().removeChild(errorIndicatorElement);
errorIndicatorElement = null;
}
}
}

+ 23
- 19
client/src/main/java/com/vaadin/client/ui/VPanel.java View File

@@ -24,11 +24,12 @@ import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.SimplePanel;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.Focusable;
import com.vaadin.client.WidgetUtil.ErrorUtil;
import com.vaadin.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner;
import com.vaadin.client.ui.TouchScrollDelegate.TouchScrollHandler;

public class VPanel extends SimplePanel
implements ShortcutActionHandlerOwner, Focusable {
public class VPanel extends SimplePanel implements ShortcutActionHandlerOwner,
Focusable, HasErrorIndicatorElement {

public static final String CLASSNAME = "v-panel";

@@ -133,23 +134,6 @@ public class VPanel extends SimplePanel
DOM.setInnerHTML(captionText, text);
}

/** For internal use only. May be removed or replaced in the future. */
public void setErrorIndicatorVisible(boolean showError) {
if (showError) {
if (errorIndicatorElement == null) {
errorIndicatorElement = DOM.createSpan();
DOM.setElementProperty(errorIndicatorElement, "className",
"v-errorindicator");
DOM.sinkEvents(errorIndicatorElement, Event.MOUSEEVENTS);
sinkEvents(Event.MOUSEEVENTS);
}
DOM.insertBefore(captionNode, errorIndicatorElement, captionText);
} else if (errorIndicatorElement != null) {
DOM.removeChild(captionNode, errorIndicatorElement);
errorIndicatorElement = null;
}
}

/** For internal use only. May be removed or replaced in the future. */
public void setIconUri(String iconUri, ApplicationConnection client) {
if (icon != null) {
@@ -201,4 +185,24 @@ public class VPanel extends SimplePanel
}
touchScrollHandler.addElement(contentNode);
}

@Override
public Element getErrorIndicatorElement() {
return errorIndicatorElement;
}

@Override
public void setErrorIndicatorElementVisible(boolean visible) {
if (visible) {
if (errorIndicatorElement == null) {
errorIndicatorElement = ErrorUtil.createErrorIndicatorElement();
DOM.sinkEvents(errorIndicatorElement, Event.MOUSEEVENTS);
sinkEvents(Event.MOUSEEVENTS);
captionNode.insertBefore(errorIndicatorElement, captionText);
}
} else if (errorIndicatorElement != null){
captionNode.removeChild(errorIndicatorElement);
errorIndicatorElement = null;
}
}
}

+ 3
- 1
client/src/main/java/com/vaadin/client/ui/VTabsheet.java View File

@@ -340,7 +340,8 @@ public class VTabsheet extends VTabsheetBase
|| tabState.componentError != null) {
setTooltipInfo(new TooltipInfo(tabState.description,
tabState.descriptionContentMode,
tabState.componentError, this));
tabState.componentError, this,
tabState.componentErrorLevel));
} else {
setTooltipInfo(null);
}
@@ -352,6 +353,7 @@ public class VTabsheet extends VTabsheetBase
boolean ret = updateCaptionWithoutOwner(captionString,
!tabState.enabled, hasAttribute(tabState.description),
hasAttribute(tabState.componentError),
tabState.componentErrorLevel,
tab.getTabsheet().connector.getResourceUrl(
ComponentConstants.ICON_RESOURCE + tabState.key),
tabState.iconAltText);

+ 2
- 16
client/src/main/java/com/vaadin/client/ui/button/ButtonConnector.java View File

@@ -20,7 +20,9 @@ import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.DOM;
import com.vaadin.client.MouseEventDetailsBuilder;
import com.vaadin.client.StyleConstants;
import com.vaadin.client.VCaption;
import com.vaadin.client.WidgetUtil.ErrorUtil;
import com.vaadin.client.annotations.OnStateChange;
import com.vaadin.client.ui.AbstractComponentConnector;
import com.vaadin.client.ui.ConnectorFocusAndBlurHandler;
@@ -50,22 +52,6 @@ public class ButtonConnector extends AbstractComponentConnector
ConnectorFocusAndBlurHandler.addHandlers(this);
}

@OnStateChange("errorMessage")
void setErrorMessage() {
if (null != getState().errorMessage) {
if (getWidget().errorIndicatorElement == null) {
getWidget().errorIndicatorElement = DOM.createSpan();
getWidget().errorIndicatorElement
.setClassName("v-errorindicator");
}
getWidget().wrapper.insertFirst(getWidget().errorIndicatorElement);

} else if (getWidget().errorIndicatorElement != null) {
getWidget().wrapper.removeChild(getWidget().errorIndicatorElement);
getWidget().errorIndicatorElement = null;
}
}

@OnStateChange("resources")
void onResourceChange() {
if (getWidget().icon != null) {

+ 3
- 21
client/src/main/java/com/vaadin/client/ui/checkbox/CheckBoxConnector.java View File

@@ -21,8 +21,10 @@ import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.vaadin.client.MouseEventDetailsBuilder;
import com.vaadin.client.StyleConstants;
import com.vaadin.client.VCaption;
import com.vaadin.client.VTooltip;
import com.vaadin.client.WidgetUtil.ErrorUtil;
import com.vaadin.client.annotations.OnStateChange;
import com.vaadin.client.communication.StateChangeEvent;
import com.vaadin.client.ui.AbstractFieldConnector;
@@ -66,27 +68,7 @@ public class CheckBoxConnector extends AbstractFieldConnector
public void onStateChanged(StateChangeEvent stateChangeEvent) {
super.onStateChanged(stateChangeEvent);

if (null != getState().errorMessage) {
getWidget().setAriaInvalid(true);

if (getWidget().errorIndicatorElement == null) {
getWidget().errorIndicatorElement = DOM.createSpan();
getWidget().errorIndicatorElement.setInnerHTML("&nbsp;");
DOM.setElementProperty(getWidget().errorIndicatorElement,
"className", "v-errorindicator");
DOM.appendChild(getWidget().getElement(),
getWidget().errorIndicatorElement);
DOM.sinkEvents(getWidget().errorIndicatorElement,
VTooltip.TOOLTIP_EVENTS | Event.ONCLICK);
} else {
getWidget().errorIndicatorElement.getStyle().clearDisplay();
}
} else if (getWidget().errorIndicatorElement != null) {
getWidget().errorIndicatorElement.getStyle()
.setDisplay(Display.NONE);

getWidget().setAriaInvalid(false);
}
getWidget().setAriaInvalid(getState().errorMessage != null);

getWidget().setAriaRequired(isRequiredIndicatorVisible());
if (isReadOnly()) {

+ 2
- 1
client/src/main/java/com/vaadin/client/ui/formlayout/FormLayoutConnector.java View File

@@ -254,7 +254,8 @@ public class FormLayoutConnector extends AbstractLayoutConnector
}

getWidget().table.updateError(component.getWidget(),
component.getState().errorMessage, hideErrors);
component.getState().errorMessage,
component.getState().errorLevel, hideErrors);
}

@Override

+ 2
- 14
client/src/main/java/com/vaadin/client/ui/link/LinkConnector.java View File

@@ -18,7 +18,9 @@ package com.vaadin.client.ui.link;

import com.google.gwt.dom.client.Style.Display;
import com.google.gwt.user.client.DOM;
import com.vaadin.client.StyleConstants;
import com.vaadin.client.VCaption;
import com.vaadin.client.WidgetUtil.ErrorUtil;
import com.vaadin.client.communication.StateChangeEvent;
import com.vaadin.client.ui.AbstractComponentConnector;
import com.vaadin.client.ui.Icon;
@@ -70,20 +72,6 @@ public class LinkConnector extends AbstractComponentConnector {
// Set link caption
VCaption.setCaptionText(getWidget().captionElement, getState());

// handle error
if (null != getState().errorMessage) {
if (getWidget().errorIndicatorElement == null) {
getWidget().errorIndicatorElement = DOM.createDiv();
DOM.setElementProperty(getWidget().errorIndicatorElement,
"className", "v-errorindicator");
}
DOM.insertChild(getWidget().getElement(),
getWidget().errorIndicatorElement, 0);
} else if (getWidget().errorIndicatorElement != null) {
getWidget().errorIndicatorElement.getStyle()
.setDisplay(Display.NONE);
}

if (getWidget().icon != null) {
getWidget().anchor.removeChild(getWidget().icon.getElement());
getWidget().icon = null;

+ 2
- 17
client/src/main/java/com/vaadin/client/ui/nativebutton/NativeButtonConnector.java View File

@@ -16,7 +16,9 @@
package com.vaadin.client.ui.nativebutton;

import com.google.gwt.user.client.DOM;
import com.vaadin.client.StyleConstants;
import com.vaadin.client.VCaption;
import com.vaadin.client.WidgetUtil.ErrorUtil;
import com.vaadin.client.communication.StateChangeEvent;
import com.vaadin.client.ui.AbstractComponentConnector;
import com.vaadin.client.ui.ConnectorFocusAndBlurHandler;
@@ -55,23 +57,6 @@ public class NativeButtonConnector extends AbstractComponentConnector {
// Set text
VCaption.setCaptionText(getWidget(), getState());

// handle error
if (null != getState().errorMessage) {
if (getWidget().errorIndicatorElement == null) {
getWidget().errorIndicatorElement = DOM.createSpan();
getWidget().errorIndicatorElement
.setClassName("v-errorindicator");
}
getWidget().getElement().insertBefore(
getWidget().errorIndicatorElement,
getWidget().captionElement);

} else if (getWidget().errorIndicatorElement != null) {
getWidget().getElement()
.removeChild(getWidget().errorIndicatorElement);
getWidget().errorIndicatorElement = null;
}

if (getWidget().icon != null) {
getWidget().getElement().removeChild(getWidget().icon.getElement());
getWidget().icon = null;

+ 3
- 2
client/src/main/java/com/vaadin/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java View File

@@ -275,8 +275,9 @@ public abstract class AbstractOrderedLayoutConnector
slot.setCaptionResizeListener(null);
}

slot.setCaption(caption, icon, styles, error, showError, required,
enabled, child.getState().captionAsHtml);
slot.setCaption(caption, icon, styles, error,
child.getState().errorLevel, showError, required, enabled,
child.getState().captionAsHtml);

AriaHelper.handleInputRequired(child.getWidget(), required);
AriaHelper.handleInputInvalid(child.getWidget(), showError);

+ 58
- 9
client/src/main/java/com/vaadin/client/ui/orderedlayout/Slot.java View File

@@ -30,16 +30,19 @@ import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.LayoutManager;
import com.vaadin.client.StyleConstants;
import com.vaadin.client.WidgetUtil;
import com.vaadin.client.WidgetUtil.ErrorUtil;
import com.vaadin.client.ui.FontIcon;
import com.vaadin.client.ui.HasErrorIndicatorElement;
import com.vaadin.client.ui.Icon;
import com.vaadin.client.ui.ImageIcon;
import com.vaadin.client.ui.layout.ElementResizeListener;
import com.vaadin.shared.ui.AlignmentInfo;
import com.vaadin.shared.ui.ErrorLevel;

/**
* Represents a slot which contains the actual widget in the layout.
*/
public class Slot extends SimplePanel {
public class Slot extends SimplePanel implements HasErrorIndicatorElement {

private static final String ALIGN_CLASS_PREFIX = "v-align-";

@@ -493,6 +496,37 @@ public class Slot extends SimplePanel {
public void setCaption(String captionText, Icon icon, List<String> styles,
String error, boolean showError, boolean required, boolean enabled,
boolean captionAsHtml) {
setCaption(captionText, icon, styles, error, null, showError, required,
enabled, captionAsHtml);
}

/**
* Set the caption of the slot
*
* @param captionText
* The text of the caption
* @param icon
* The icon
* @param styles
* The style names
* @param error
* The error message
* @param errorLevel
* The error level
* @param showError
* Should the error message be shown
* @param required
* Is the (field) required
* @param enabled
* Is the component enabled
* @param captionAsHtml
* true if the caption should be rendered as HTML, false
* otherwise
* @since 8.2
*/
public void setCaption(String captionText, Icon icon, List<String> styles,
String error, ErrorLevel errorLevel, boolean showError,
boolean required, boolean enabled, boolean captionAsHtml) {

// TODO place for optimization: check if any of these have changed
// since last time, and only run those changes
@@ -583,14 +617,11 @@ public class Slot extends SimplePanel {

// Error
if (error != null && showError) {
if (errorIcon == null) {
errorIcon = DOM.createSpan();
errorIcon.setClassName("v-errorindicator");
}
caption.appendChild(errorIcon);
} else if (errorIcon != null) {
errorIcon.removeFromParent();
errorIcon = null;
setErrorIndicatorElementVisible(true);
ErrorUtil.setErrorLevelStyle(getErrorIndicatorElement(),
StyleConstants.STYLE_NAME_ERROR_INDICATOR, errorLevel);
} else {
setErrorIndicatorElementVisible(false);
}

if (caption != null) {
@@ -799,4 +830,22 @@ public class Slot extends SimplePanel {
return hasRelativeWidth();
}
}

@Override
public Element getErrorIndicatorElement() {
return errorIcon;
}

@Override
public void setErrorIndicatorElementVisible(boolean visible) {
if (visible) {
if (errorIcon == null) {
errorIcon = ErrorUtil.createErrorIndicatorElement();
}
caption.appendChild(errorIcon);
} else if (errorIcon != null) {
errorIcon.removeFromParent();
errorIcon = null;
}
}
}

+ 0
- 2
client/src/main/java/com/vaadin/client/ui/panel/PanelConnector.java View File

@@ -143,8 +143,6 @@ public class PanelConnector extends AbstractSingleComponentContainerConnector
getWidget().setIconUri(null, client);
}

getWidget().setErrorIndicatorVisible(isErrorIndicatorVisible());

// We may have actions attached to this panel
if (uidl.getChildCount() > 0) {
final int cnt = uidl.getChildCount();

+ 8
- 1
compatibility-client/src/main/java/com/vaadin/v7/client/ui/checkbox/CheckBoxConnector.java View File

@@ -21,8 +21,10 @@ import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.vaadin.client.MouseEventDetailsBuilder;
import com.vaadin.client.StyleConstants;
import com.vaadin.client.VCaption;
import com.vaadin.client.VTooltip;
import com.vaadin.client.WidgetUtil.ErrorUtil;
import com.vaadin.client.annotations.OnStateChange;
import com.vaadin.client.communication.StateChangeEvent;
import com.vaadin.client.ui.ConnectorFocusAndBlurHandler;
@@ -67,7 +69,7 @@ public class CheckBoxConnector extends AbstractFieldConnector
getWidget().errorIndicatorElement = DOM.createSpan();
getWidget().errorIndicatorElement.setInnerHTML("&nbsp;");
DOM.setElementProperty(getWidget().errorIndicatorElement,
"className", "v-errorindicator");
"className", StyleConstants.STYLE_NAME_ERROR_INDICATOR);
DOM.appendChild(getWidget().getElement(),
getWidget().errorIndicatorElement);
DOM.sinkEvents(getWidget().errorIndicatorElement,
@@ -75,6 +77,11 @@ public class CheckBoxConnector extends AbstractFieldConnector
} else {
getWidget().errorIndicatorElement.getStyle().clearDisplay();
}

ErrorUtil.setErrorLevelStyle(getWidget().errorIndicatorElement,
StyleConstants.STYLE_NAME_ERROR_INDICATOR,
getState().errorLevel);

} else if (getWidget().errorIndicatorElement != null) {
getWidget().errorIndicatorElement.getStyle()
.setDisplay(Display.NONE);

+ 1
- 0
compatibility-client/src/main/java/com/vaadin/v7/client/ui/form/FormConnector.java View File

@@ -126,6 +126,7 @@ public class FormConnector extends AbstractComponentContainerConnector

if (null != getState().errorMessage) {
getWidget().errorMessage.updateMessage(getState().errorMessage);
getWidget().errorMessage.updateErrorLevel(getState().errorLevel);
getWidget().errorMessage.setVisible(true);
} else {
getWidget().errorMessage.setVisible(false);

+ 1
- 1
compatibility-server/src/main/java/com/vaadin/v7/data/Buffered.java View File

@@ -21,9 +21,9 @@ import java.io.Serializable;
import com.vaadin.data.Binder;
import com.vaadin.server.AbstractErrorMessage;
import com.vaadin.server.ErrorMessage;
import com.vaadin.server.ErrorMessage.ErrorLevel;
import com.vaadin.server.ErrorMessageProducer;
import com.vaadin.server.UserError;
import com.vaadin.shared.ui.ErrorLevel;
import com.vaadin.v7.data.Validator.InvalidValueException;

/**

+ 1
- 1
compatibility-server/src/main/java/com/vaadin/v7/data/Validator.java View File

@@ -22,10 +22,10 @@ import com.vaadin.data.Binder;
import com.vaadin.server.AbstractErrorMessage;
import com.vaadin.server.AbstractErrorMessage.ContentMode;
import com.vaadin.server.ErrorMessage;
import com.vaadin.server.ErrorMessage.ErrorLevel;
import com.vaadin.server.ErrorMessageProducer;
import com.vaadin.server.UserError;
import com.vaadin.server.VaadinServlet;
import com.vaadin.shared.ui.ErrorLevel;

/**
* Interface that implements a method for validating if an {@link Object} is

+ 1
- 0
compatibility-server/src/main/java/com/vaadin/v7/ui/Grid.java View File

@@ -58,6 +58,7 @@ import com.vaadin.server.VaadinSession;
import com.vaadin.shared.MouseEventDetails;
import com.vaadin.shared.Registration;
import com.vaadin.shared.data.sort.SortDirection;
import com.vaadin.shared.ui.ErrorLevel;
import com.vaadin.shared.util.SharedUtil;
import com.vaadin.ui.AbstractComponent;
import com.vaadin.ui.Component;

+ 1
- 1
compatibility-server/src/test/java/com/vaadin/v7/ui/AbstractLegacyComponentDeclarativeTest.java View File

@@ -29,12 +29,12 @@ import org.jsoup.parser.Tag;
import org.junit.Before;
import org.junit.Test;

import com.vaadin.server.ErrorMessage.ErrorLevel;
import com.vaadin.server.ExternalResource;
import com.vaadin.server.FileResource;
import com.vaadin.server.Responsive;
import com.vaadin.server.ThemeResource;
import com.vaadin.server.UserError;
import com.vaadin.shared.ui.ErrorLevel;
import com.vaadin.tests.design.DeclarativeTestBase;
import com.vaadin.ui.AbstractComponent;
import com.vaadin.ui.Label;

+ 2
- 0
server/src/main/java/com/vaadin/server/AbstractErrorMessage.java View File

@@ -21,6 +21,8 @@ import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;

import com.vaadin.shared.ui.ErrorLevel;

/**
* Base class for component error messages.
*

+ 4
- 2
server/src/main/java/com/vaadin/server/CompositeErrorMessage.java View File

@@ -19,6 +19,8 @@ package com.vaadin.server;
import java.util.Collection;
import java.util.Iterator;

import com.vaadin.shared.ui.ErrorLevel;

/**
* Class for combining multiple error messages together.
*
@@ -37,7 +39,7 @@ public class CompositeErrorMessage extends AbstractErrorMessage {
*/
public CompositeErrorMessage(ErrorMessage... errorMessages) {
super(null);
setErrorLevel(ErrorLevel.INFORMATION);
setErrorLevel(ErrorLevel.INFO);

for (ErrorMessage errorMessage : errorMessages) {
addErrorMessage(errorMessage);
@@ -60,7 +62,7 @@ public class CompositeErrorMessage extends AbstractErrorMessage {
public CompositeErrorMessage(
Collection<? extends ErrorMessage> errorMessages) {
super(null);
setErrorLevel(ErrorLevel.INFORMATION);
setErrorLevel(ErrorLevel.INFO);

for (ErrorMessage errorMessage : errorMessages) {
addErrorMessage(errorMessage);

+ 6
- 59
server/src/main/java/com/vaadin/server/ErrorMessage.java View File

@@ -18,6 +18,8 @@ package com.vaadin.server;

import java.io.Serializable;

import com.vaadin.shared.ui.ErrorLevel;

/**
* Interface for rendering error messages to terminal. All the visible errors
* shown to user must implement this interface.
@@ -27,66 +29,11 @@ import java.io.Serializable;
*/
public interface ErrorMessage extends Serializable {

public enum ErrorLevel {
/**
* Error code for informational messages.
*/
INFORMATION("info", 0),
/**
* Error code for warning messages.
*/
WARNING("warning", 1),
/**
* Error code for regular error messages.
*/
ERROR("error", 2),
/**
* Error code for critical error messages.
*/
CRITICAL("critical", 3),
/**
* Error code for system errors and bugs.
*/
SYSTEMERROR("system", 4);

String text;
int errorLevel;

private ErrorLevel(String text, int errorLevel) {
this.text = text;
this.errorLevel = errorLevel;
}

/**
* Textual representation for server-client communication of level
*
* @return String for error severity
*/
public String getText() {
return text;
}

/**
* Integer representation of error severity for comparison
*
* @return integer for error severity
*/
public int intValue() {
return errorLevel;
}

@Override
public String toString() {
return text;
}

}

/**
* @deprecated As of 7.0, use {@link ErrorLevel#SYSTEMERROR} instead    
* @deprecated As of 7.0, use {@link ErrorLevel#SYSTEM} instead    
*/
@Deprecated
public static final ErrorLevel SYSTEMERROR = ErrorLevel.SYSTEMERROR;
public static final ErrorLevel SYSTEMERROR = ErrorLevel.SYSTEM;

/**
* @deprecated As of 7.0, use {@link ErrorLevel#CRITICAL} instead    
@@ -108,10 +55,10 @@ public interface ErrorMessage extends Serializable {
public static final ErrorLevel WARNING = ErrorLevel.WARNING;

/**
* @deprecated As of 7.0, use {@link ErrorLevel#INFORMATION} instead    
* @deprecated As of 7.0, use {@link ErrorLevel#INFO} instead    
*/
@Deprecated
public static final ErrorLevel INFORMATION = ErrorLevel.INFORMATION;
public static final ErrorLevel INFORMATION = ErrorLevel.INFO;

/**
* Gets the errors level.

+ 3
- 1
server/src/main/java/com/vaadin/server/SystemError.java View File

@@ -16,6 +16,8 @@

package com.vaadin.server;

import com.vaadin.shared.ui.ErrorLevel;

/**
* <code>SystemError</code> is an error message for a problem caused by error in
* system, not the user application code. The system error can contain technical
@@ -39,7 +41,7 @@ public class SystemError extends AbstractErrorMessage {
*/
public SystemError(String message) {
super(message);
setErrorLevel(ErrorLevel.SYSTEMERROR);
setErrorLevel(ErrorLevel.SYSTEM);
setMode(ContentMode.HTML);
setMessage(getHtmlMessage());
}

+ 2
- 0
server/src/main/java/com/vaadin/server/UserError.java View File

@@ -16,6 +16,8 @@

package com.vaadin.server;

import com.vaadin.shared.ui.ErrorLevel;

/**
* <code>UserError</code> is a controlled error occurred in application. User
* errors are occur in normal usage of the application and guide the user.

+ 3
- 1
server/src/main/java/com/vaadin/ui/AbstractComponent.java View File

@@ -45,7 +45,6 @@ import com.vaadin.server.AbstractClientConnector;
import com.vaadin.server.ClientConnector;
import com.vaadin.server.ComponentSizeValidator;
import com.vaadin.server.ErrorMessage;
import com.vaadin.server.ErrorMessage.ErrorLevel;
import com.vaadin.server.Extension;
import com.vaadin.server.Resource;
import com.vaadin.server.Responsive;
@@ -62,6 +61,7 @@ import com.vaadin.shared.MouseEventDetails;
import com.vaadin.shared.Registration;
import com.vaadin.shared.ui.ComponentStateUtil;
import com.vaadin.shared.ui.ContentMode;
import com.vaadin.shared.ui.ErrorLevel;
import com.vaadin.shared.util.SharedUtil;
import com.vaadin.ui.declarative.DesignAttributeHandler;
import com.vaadin.ui.declarative.DesignContext;
@@ -723,8 +723,10 @@ public abstract class AbstractComponent extends AbstractClientConnector
ErrorMessage error = getErrorMessage();
if (null != error) {
getState().errorMessage = error.getFormattedHtmlMessage();
getState().errorLevel = error.getErrorLevel();
} else {
getState().errorMessage = null;
getState().errorLevel = null;
}
}


+ 9
- 3
server/src/main/java/com/vaadin/ui/TabSheet.java View File

@@ -1314,9 +1314,15 @@ public class TabSheet extends AbstractComponentContainer
public void setComponentError(ErrorMessage componentError) {
this.componentError = componentError;

String formattedHtmlMessage = componentError != null
? componentError.getFormattedHtmlMessage() : null;
tabState.componentError = formattedHtmlMessage;
if (componentError != null) {
tabState.componentError = componentError
.getFormattedHtmlMessage();
tabState.componentErrorLevel = componentError.getErrorLevel();
} else {
tabState.componentError = null;
tabState.componentErrorLevel = null;
}


markAsDirty();
}

+ 1
- 1
server/src/test/java/com/vaadin/tests/server/component/abstractcomponent/AbstractComponentDeclarativeTest.java View File

@@ -28,13 +28,13 @@ import org.jsoup.parser.Tag;
import org.junit.Before;
import org.junit.Test;

import com.vaadin.server.ErrorMessage.ErrorLevel;
import com.vaadin.shared.ui.ContentMode;
import com.vaadin.server.ExternalResource;
import com.vaadin.server.FileResource;
import com.vaadin.server.Responsive;
import com.vaadin.server.ThemeResource;
import com.vaadin.server.UserError;
import com.vaadin.shared.ui.ErrorLevel;
import com.vaadin.tests.design.DeclarativeTestBase;
import com.vaadin.ui.AbstractComponent;
import com.vaadin.ui.Button;

+ 1
- 1
server/src/test/java/com/vaadin/tests/server/component/abstractcomponent/AbstractComponentDeclarativeTestBase.java View File

@@ -24,11 +24,11 @@ import java.util.Locale;

import org.junit.Test;

import com.vaadin.server.ErrorMessage.ErrorLevel;
import com.vaadin.server.ExternalResource;
import com.vaadin.server.FileResource;
import com.vaadin.server.ThemeResource;
import com.vaadin.server.UserError;
import com.vaadin.shared.ui.ErrorLevel;
import com.vaadin.tests.design.DeclarativeTestBase;
import com.vaadin.ui.AbstractComponent;
import com.vaadin.ui.declarative.DesignContext;

+ 10
- 3
shared/src/main/java/com/vaadin/shared/AbstractComponentState.java View File

@@ -21,6 +21,7 @@ import java.util.List;
import com.vaadin.shared.annotations.NoLayout;
import com.vaadin.shared.communication.SharedState;
import com.vaadin.shared.ui.ContentMode;
import com.vaadin.shared.ui.ErrorLevel;

/**
* Default shared state implementation for AbstractComponent.
@@ -43,9 +44,15 @@ public class AbstractComponentState extends SharedState {
public String id = null;
public String primaryStyleName = null;

// HTML formatted error message for the component
// TODO this could be an object with more information, but currently the UI
// only uses the message
/** HTML formatted error message for the component */
public String errorMessage = null;

/**
* Level of error
*
* @since 8.2
*/
public ErrorLevel errorLevel = null;

public boolean captionAsHtml = false;
}

+ 58
- 0
shared/src/main/java/com/vaadin/shared/ui/ErrorLevel.java View File

@@ -0,0 +1,58 @@
/*
* Copyright 2000-2016 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.shared.ui;

/**
* Represents the error levels displayed on components.
* @author Vaadin Ltd
* @since
*/
public enum ErrorLevel {

/**
* Error level for informational messages.
*/
INFO,

/**
* Error level for warning messages.
*/
WARNING,

/**
* Error level for regular messages.
*/
ERROR,

/**
* Error level for critical messages.
*/
CRITICAL,

/**
* Error level for system errors and bugs.
*/
SYSTEM;

/**
* Integer representation of error severity for comparison
*
* @return integer for error severity
*/
public int intValue() {
return ordinal();
}
}

+ 9
- 0
shared/src/main/java/com/vaadin/shared/ui/tabsheet/TabState.java View File

@@ -19,6 +19,8 @@ import java.io.Serializable;

import com.vaadin.shared.ui.ContentMode;

import com.vaadin.shared.ui.ErrorLevel;

/**
* Shared state of a single tab in a Tabsheet or an Accordion.
*
@@ -36,6 +38,13 @@ public class TabState implements Serializable {
public String styleName;
public String key;
public String componentError;

/**
* Represents the level of error on a tab.
*
* @since 8.2
*/
public ErrorLevel componentErrorLevel;
public String id;
public String iconAltText;


+ 1
- 1
tests/screenshots

@@ -1 +1 @@
Subproject commit 3a4a26065d36ac5fb706c159f1dd757fb52bf8eb
Subproject commit 37f67cd836df7d34469f55298bb167696338f2a5

+ 55
- 0
themes/src/main/themes/VAADIN/themes/valo/components/_combobox.scss View File

@@ -35,6 +35,61 @@
}
}

.#{$primary-stylename}-error-info {
.#{$primary-stylename}-input {
@include valo-textfield-error-level-info-style;
}

.#{$primary-stylename}-button {
color: $v-error-indicator-level-info-color;
border-color: $v-error-indicator-level-info-color;
}
}

.#{$primary-stylename}-error-warning {
.#{$primary-stylename}-input {
@include valo-textfield-error-level-warning-style;
}

.#{$primary-stylename}-button {
color: $v-error-indicator-level-warning-color;
border-color: $v-error-indicator-level-warning-color;
}
}

.#{$primary-stylename}-error-error {
.#{$primary-stylename}-input {
@include valo-textfield-error-level-error-style;
}

.#{$primary-stylename}-button {
color: $v-error-indicator-level-error-color;
border-color: $v-error-indicator-level-error-color;
}
}

.#{$primary-stylename}-error-critical {
.#{$primary-stylename}-input {
@include valo-textfield-error-level-critical-style;
}

.#{$primary-stylename}-button {
color: $v-error-indicator-level-critical-color;
border-color: $v-error-indicator-level-critical-color;
}
}

.#{$primary-stylename}-error-system {
.#{$primary-stylename}-input {
@include valo-textfield-error-level-system-style;
}

.#{$primary-stylename}-button {
color: $v-error-indicator-level-system-color;
border-color: $v-error-indicator-level-system-color;
}
}

.#{$primary-stylename}-suggestpopup {
@include valo-combobox-popup-style;
}

+ 55
- 0
themes/src/main/themes/VAADIN/themes/valo/components/_datefield.scss View File

@@ -25,6 +25,61 @@
}
}

.#{$primary-stylename}-error-info {
.#{$primary-stylename}-textfield {
@include valo-textfield-error-level-info-style;
}

.#{$primary-stylename}-button {
color: $v-error-indicator-level-info-color;
border-color: $v-error-indicator-level-info-color;
}
}

.#{$primary-stylename}-error-warning {
.#{$primary-stylename}-textfield {
@include valo-textfield-error-level-warning-style;
}

.#{$primary-stylename}-button {
color: $v-error-indicator-level-warning-color;
border-color: $v-error-indicator-level-warning-color;
}
}

.#{$primary-stylename}-error-error {
.#{$primary-stylename}-textfield {
@include valo-textfield-error-level-error-style;
}

.#{$primary-stylename}-button {
color: $v-error-indicator-level-error-color;
border-color: $v-error-indicator-level-error-color;
}
}

.#{$primary-stylename}-error-critical {
.#{$primary-stylename}-textfield {
@include valo-textfield-error-level-critical-style;
}

.#{$primary-stylename}-button {
color: $v-error-indicator-level-critical-color;
border-color: $v-error-indicator-level-critical-color;
}
}

.#{$primary-stylename}-error-system {
.#{$primary-stylename}-textfield {
@include valo-textfield-error-level-system-style;
}

.#{$primary-stylename}-button {
color: $v-error-indicator-level-system-color;
border-color: $v-error-indicator-level-system-color;
}
}

// Different widths for different resolutions
.#{$primary-stylename}-full {
width: round($v-font-size * 15);

+ 67
- 3
themes/src/main/themes/VAADIN/themes/valo/components/_textfield.scss View File

@@ -70,6 +70,25 @@ $v-textfield-disabled-opacity: $v-disabled-opacity !default;
@include valo-textfield-error-style;
}

.#{$primary-stylename}-error-info {
@include valo-textfield-error-level-info-style;
}

.#{$primary-stylename}-error-warning {
@include valo-textfield-error-level-warning-style;
}

.#{$primary-stylename}-error-error {
@include valo-textfield-error-level-error-style;
}

.#{$primary-stylename}-error-critical {
@include valo-textfield-error-level-critical-style;
}

.#{$primary-stylename}-error-system {
@include valo-textfield-error-level-system-style;
}

@if $include-additional-styles {
.#{$primary-stylename}-borderless {
@@ -319,13 +338,58 @@ $v-textfield-disabled-opacity: $v-disabled-opacity !default;
*
* @group textfield
*/
@mixin valo-textfield-error-style {
border-color: $v-error-indicator-color !important;
$bg: scale-color($v-error-indicator-color, $lightness: 98%);
@mixin valo-textfield-error-style($indicator-color: $v-error-indicator-color) {
border-color: $indicator-color !important;
$bg: scale-color($indicator-color, $lightness: 98%);
background: $bg;
color: valo-font-color($bg);
}

/**
* Outputs the styles for a text field error state with error level 'info'.
*
* @group textfield
*/
@mixin valo-textfield-error-level-info-style {
@include valo-textfield-error-style($v-error-indicator-level-info-color);
}

/**
* Outputs the styles for a text field error state with error level 'warning'.
*
* @group textfield
*/
@mixin valo-textfield-error-level-warning-style {
@include valo-textfield-error-style($v-error-indicator-level-warning-color);
}

/**
* Outputs the styles for a text field error state with error level 'error'.
*
* @group textfield
*/
@mixin valo-textfield-error-level-error-style {
@include valo-textfield-error-style($v-error-indicator-level-error-color);
}

/**
* Outputs the styles for a text field error state with error level 'critical'.
*
* @group textfield
*/
@mixin valo-textfield-error-level-critical-style {
@include valo-textfield-error-style($v-error-indicator-level-critical-color);
}

/**
* Outputs the styles for a text field error state with error level 'system'.
*
* @group textfield
*/
@mixin valo-textfield-error-level-system-style {
@include valo-textfield-error-style($v-error-indicator-level-system-color);
}


/**
* Outputs the selectors and styles for an inline-icon style for a text field. Included indipendently (i.e. not enclosed with a parent text field selector).

+ 34
- 0
themes/src/main/themes/VAADIN/themes/valo/components/_twincolselect.scss View File

@@ -85,6 +85,40 @@
}
}

.#{$primary-stylename}-error-info {
.#{$primary-stylename}-options,
.#{$primary-stylename}-selections {
@include valo-textfield-error-level-info-style;
}
}

.#{$primary-stylename}-error-warning {
.#{$primary-stylename}-options,
.#{$primary-stylename}-selections {
@include valo-textfield-error-level-warning-style;
}
}

.#{$primary-stylename}-error-error {
.#{$primary-stylename}-options,
.#{$primary-stylename}-selections {
@include valo-textfield-error-level-error-style;
}
}

.#{$primary-stylename}-error-critical {
.#{$primary-stylename}-options,
.#{$primary-stylename}-selections {
@include valo-textfield-error-level-critical-style;
}
}

.#{$primary-stylename}-error-system {
.#{$primary-stylename}-options,
.#{$primary-stylename}-selections {
@include valo-textfield-error-level-system-style;
}
}
}



+ 22
- 2
themes/src/main/themes/VAADIN/themes/valo/shared/_global.scss View File

@@ -313,6 +313,26 @@ $valo-shared-pathPrefix: null;
@include valo-error-indicator-style;
}

.v-errorindicator-info {
@include valo-error-indicator-style($indicator-color: $v-error-indicator-level-info-color);
}

.v-errorindicator-warning {
@include valo-error-indicator-style($indicator-color: $v-error-indicator-level-warning-color);
}

.v-errorindicator-error {
@include valo-error-indicator-style($indicator-color: $v-error-indicator-level-error-color);
}

.v-errorindicator-critical {
@include valo-error-indicator-style($indicator-color: $v-error-indicator-level-critical-color);
}

.v-errorindicator-system {
@include valo-error-indicator-style($indicator-color: $v-error-indicator-level-system-color);
}

.v-required-field-indicator {
color: $v-required-field-indicator-color;
padding: 0 .2em;
@@ -366,8 +386,8 @@ $valo-shared-pathPrefix: null;
*
* @requires {mixin} valo-error-indicator-icon-style by default
*/
@mixin valo-error-indicator-style ($is-pseudo-element: false) {
color: $v-error-indicator-color;
@mixin valo-error-indicator-style ($is-pseudo-element: false, $indicator-color: $v-error-indicator-color) {
color: $indicator-color;
font-weight: 600;
width: ceil($v-unit-size/2);
text-align: center;

+ 60
- 0
themes/src/main/themes/VAADIN/themes/valo/shared/_tooltip.scss View File

@@ -62,6 +62,46 @@ $v-tooltip-error-message-background-color: #fff !default;
*/
$v-tooltip-error-message-font-color: $v-error-indicator-color !default;

/**
* The font color for error tooltips for level 'info'.
*
* @type color
* @group tooltip
*/
$v-tooltip-error-message-level-info-font-color: $v-error-indicator-level-info-color !default;

/**
* The font color for error tooltips for level 'warning'.
*
* @type color
* @group tooltip
*/
$v-tooltip-error-message-level-warning-font-color: $v-error-indicator-level-warning-color !default;

/**
* The font color for error tooltips for level 'error'.
*
* @type color
* @group tooltip
*/
$v-tooltip-error-message-level-error-font-color: $v-error-indicator-level-error-color !default;

/**
* The font color for error tooltips for level 'critical'.
*
* @type color
* @group tooltip
*/
$v-tooltip-error-message-level-critical-font-color: $v-error-indicator-level-critical-color !default;

/**
* The font color for error tooltips for level 'system'.
*
* @type color
* @group tooltip
*/
$v-tooltip-error-message-level-system-font-color: $v-error-indicator-level-system-color !default;

/**
* The corner radius for tooltips.
*
@@ -100,6 +140,26 @@ $v-tooltip-border-radius: $v-border-radius - 1px !default;
}
}

.v-errormessage-info {
color: $v-tooltip-error-message-level-info-font-color;
}

.v-errormessage-warning {
color: $v-tooltip-error-message-level-warning-font-color;
}

.v-errormessage-error {
color: $v-tooltip-error-message-level-error-font-color;
}

.v-errormessage-critical {
color: $v-tooltip-error-message-level-critical-font-color;
}

.v-errormessage-system {
color: $v-tooltip-error-message-level-system-font-color;
}

.v-tooltip-text {
max-height: 10em;
overflow: auto;

+ 41
- 1
themes/src/main/themes/VAADIN/themes/valo/shared/_variables.scss View File

@@ -206,6 +206,46 @@ $v-disabled-opacity: 0.5 !default;
*/
$v-selection-color: $v-focus-color !default;

/**
* Color of the component error indication for 'info' error level.

* @group color
* @type color
*/
$v-error-indicator-level-info-color: #00a7f5 !default;

/**
* Color of the component error indication for 'warning' error level.

* @group color
* @type color
*/
$v-error-indicator-level-warning-color: #fc9c00 !default;

/**
* Color of the component error indication for 'error' error level.

* @group color
* @type color
*/
$v-error-indicator-level-error-color: #ed473b !default;

/**
* Color of the component error indication for 'critical' error level.

* @group color
* @type color
*/
$v-error-indicator-level-critical-color: #fa007d !default;

/**
* Color of the component error indication for 'system' error level.

* @group color
* @type color
*/
$v-error-indicator-level-system-color: #bb00ff !default;

/**
* Color of the component error indicator and other error indications, such as the
* error style notification.
@@ -213,7 +253,7 @@ $v-selection-color: $v-focus-color !default;
* @group color
* @type color
*/
$v-error-indicator-color: #ed473b !default;
$v-error-indicator-color: $v-error-indicator-level-error-color !default;

/**
* Color of the required indicator in field components.

+ 210
- 0
uitest/src/main/java/com/vaadin/tests/components/ErrorLevels.java View File

@@ -0,0 +1,210 @@
package com.vaadin.tests.components;

import java.util.Arrays;

import com.vaadin.annotations.Theme;
import com.vaadin.server.AbstractErrorMessage;
import com.vaadin.server.ErrorMessage;
import com.vaadin.server.ExternalResource;
import com.vaadin.server.UserError;
import com.vaadin.server.VaadinRequest;
import com.vaadin.shared.ui.ErrorLevel;
import com.vaadin.ui.Accordion;
import com.vaadin.ui.Button;
import com.vaadin.ui.CheckBox;
import com.vaadin.ui.ComboBox;
import com.vaadin.ui.DateField;
import com.vaadin.ui.FormLayout;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.Link;
import com.vaadin.ui.NativeButton;
import com.vaadin.ui.Panel;
import com.vaadin.ui.TabSheet;
import com.vaadin.ui.TextField;
import com.vaadin.ui.TwinColSelect;
import com.vaadin.ui.themes.ValoTheme;

@Theme("valo")
public class ErrorLevels extends AbstractTestUI {

private ComboBox<ErrorLevel> errorLevels;
private Button button;
private Button borderlessButton;
private Link link;
private ComboBox<String> comboBox;
private TextField textField;
private TextField textFieldBorderless;
private TabSheet tabSheet;
private Accordion accordion;
private CheckBox checkBox;
private NativeButton nativeButton;
private FormLayout formLayout;
private TextField formLayoutTextField;
private Panel panel;
private DateField dateField;
private TwinColSelect twinColSelect;

private com.vaadin.v7.ui.ComboBox comboBoxCompat;
private com.vaadin.v7.ui.TextField textFieldCompat;
private com.vaadin.v7.ui.CheckBox checkBoxCompat;
private com.vaadin.v7.ui.DateField dateFieldCompat;
private com.vaadin.v7.ui.TwinColSelect twinColSelectCompat;

@Override
protected void setup(VaadinRequest request) {

errorLevels = new ComboBox<>("Error level",
Arrays.asList(ErrorLevel.values()));
errorLevels.setEmptySelectionAllowed(false);
errorLevels.setValue(ErrorLevel.ERROR);
errorLevels.addValueChangeListener(event -> setErrorMessages());
addComponent(errorLevels);

Label subtitle = new Label("Components");
subtitle.setStyleName(ValoTheme.LABEL_H3);
addComponent(subtitle);

// Button
button = new Button("Button");

borderlessButton = new Button("Borderless button");
borderlessButton.setStyleName(ValoTheme.BUTTON_BORDERLESS);

addComponent(new HorizontalLayout(button, borderlessButton));

// Native button
nativeButton = new NativeButton("Native button");
addComponent(nativeButton);

// Link
link = new Link("Link", new ExternalResource("#"));
addComponent(link);

// Combo box
comboBox = new ComboBox<>("Combo box");
addComponent(comboBox);

// Text field
textField = new TextField("Text field");
textField.setValue("text");

textFieldBorderless = new TextField("Borderless text field");
textFieldBorderless.setStyleName(ValoTheme.TEXTFIELD_BORDERLESS);
textFieldBorderless.setValue("text");

addComponent(new HorizontalLayout(textField, textFieldBorderless));

// Date field
dateField = new DateField("Date field");
addComponent(dateField);

// Check box
checkBox = new CheckBox("Check box");
addComponent(checkBox);

// Tab sheet
tabSheet = new TabSheet();
tabSheet.addTab(new Label("Label1"), "Tab1");
tabSheet.addTab(new Label("Label2"), "Tab2");
tabSheet.setWidth("400px");
addComponent(tabSheet);

// Accordion
accordion = new Accordion();
accordion.addTab(new Label("Label1"), "Tab1");
accordion.addTab(new Label("Label2"), "Tab2");
accordion.setWidth("400px");
addComponent(accordion);

// Form layout
formLayout = new FormLayout();
formLayout.setWidth("400px");

formLayoutTextField = new TextField("Form layout text field");
formLayout.addComponent(formLayoutTextField);

addComponent(formLayout);

// Panel
panel = new Panel();
panel.setContent(new Label("Panel"));
panel.setWidth("400px");
addComponent(panel);

// TwinColSelect
twinColSelect = new TwinColSelect("Twin col select");
addComponent(twinColSelect);

Label subtitleCompat = new Label("Compatibility components");
subtitleCompat.setStyleName(ValoTheme.LABEL_H3);
addComponent(subtitleCompat);

// Compatibility combo box
comboBoxCompat = new com.vaadin.v7.ui.ComboBox(
"Compatibility combo box");
addComponent(comboBoxCompat);

// Compatibility text field
textFieldCompat = new com.vaadin.v7.ui.TextField(
"Compatibility text field");
textFieldCompat.setValue("text");

// Compatibility check box
checkBoxCompat = new com.vaadin.v7.ui.CheckBox("Check box");
addComponent(checkBoxCompat);

// Compatibility date field
dateFieldCompat = new com.vaadin.v7.ui.DateField("Date field");
addComponent(dateFieldCompat);

// Compatibility twin col select
twinColSelectCompat = new com.vaadin.v7.ui.TwinColSelect(
"Twin col select");
addComponent(twinColSelectCompat);

setErrorMessages();

getLayout().setSpacing(true);
}

private void setErrorMessages() {
button.setComponentError(createErrorMessage("Button error"));
borderlessButton.setComponentError(
createErrorMessage("Borderless button error"));
link.setComponentError(createErrorMessage("Link error"));
comboBox.setComponentError(createErrorMessage("ComboBox error"));
textField.setComponentError(createErrorMessage("Text field error"));
textFieldBorderless.setComponentError(
createErrorMessage("Borderless text field error"));
tabSheet.setComponentError(createErrorMessage("Tab sheet error"));
tabSheet.getTab(0).setComponentError(createErrorMessage("Tab error"));
accordion.setComponentError(createErrorMessage("Accordion error"));
accordion.getTab(0).setComponentError(createErrorMessage("Tab error"));
checkBox.setComponentError(createErrorMessage("Check box error"));
nativeButton
.setComponentError(createErrorMessage("Native button error"));
formLayout.setComponentError(createErrorMessage("Form layout error"));
formLayoutTextField.setComponentError(
createErrorMessage("Form layout text field error"));
panel.setComponentError(createErrorMessage("Panel error"));
dateField.setComponentError(createErrorMessage("Date field error"));
twinColSelect
.setComponentError(createErrorMessage("Twin col select error"));
comboBoxCompat.setComponentError(
createErrorMessage("Compatibility combo box error"));
textFieldCompat.setComponentError(
createErrorMessage("Compatibility text field error"));
checkBoxCompat.setComponentError(
createErrorMessage("Compatibility check box error"));
dateFieldCompat.setComponentError(
createErrorMessage("Compatibility date field error"));
twinColSelectCompat.setComponentError(
createErrorMessage("Compatibility twin col select error"));
}

private ErrorMessage createErrorMessage(String text) {
return new UserError(text, AbstractErrorMessage.ContentMode.TEXT,
errorLevels.getValue());
}
}

+ 1
- 1
uitest/src/main/java/com/vaadin/tests/themes/valo/CommonParts.java View File

@@ -20,11 +20,11 @@ import com.vaadin.icons.VaadinIcons;
import com.vaadin.navigator.View;
import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent;
import com.vaadin.server.AbstractErrorMessage;
import com.vaadin.server.ErrorMessage.ErrorLevel;
import com.vaadin.server.Page;
import com.vaadin.server.UserError;
import com.vaadin.shared.Position;
import com.vaadin.shared.ui.ContentMode;
import com.vaadin.shared.ui.ErrorLevel;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;

+ 160
- 0
uitest/src/test/java/com/vaadin/tests/components/ErrorLevelsTest.java View File

@@ -0,0 +1,160 @@
package com.vaadin.tests.components;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import org.junit.Assert;
import org.junit.Test;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebElement;

import com.vaadin.client.StyleConstants;
import com.vaadin.shared.ui.ErrorLevel;
import com.vaadin.testbench.By;
import com.vaadin.testbench.elements.AccordionElement;
import com.vaadin.testbench.elements.ButtonElement;
import com.vaadin.testbench.elements.CheckBoxElement;
import com.vaadin.testbench.elements.ComboBoxElement;
import com.vaadin.testbench.elements.DateFieldElement;
import com.vaadin.testbench.elements.FormLayoutElement;
import com.vaadin.testbench.elements.LinkElement;
import com.vaadin.testbench.elements.NativeButtonElement;
import com.vaadin.testbench.elements.PanelElement;
import com.vaadin.testbench.elements.TabSheetElement;
import com.vaadin.testbench.elements.TwinColSelectElement;
import com.vaadin.testbench.parallel.BrowserUtil;
import com.vaadin.tests.tb3.SingleBrowserTest;

public class ErrorLevelsTest extends SingleBrowserTest {

private ComboBoxElement errorLevelSelector;

@Override
public void setup() throws Exception {
super.setup();
openTestURL();

errorLevelSelector = $(ComboBoxElement.class).first();
}

@Test
public void testErrorIndicatorsClassName() {
ErrorLevel errorLevel = ErrorLevel.WARNING;
selectErrorLevel(errorLevel);

List<WebElement> errorIndicators = findElements(
By.className(StyleConstants.STYLE_NAME_ERROR_INDICATOR));
for (WebElement errorIndicator : errorIndicators) {
assertHasRightClassNames(errorIndicator,
StyleConstants.STYLE_NAME_ERROR_INDICATOR, errorLevel);
}
}

@Test
public void testComponentsClassName() {
ErrorLevel errorLevel = ErrorLevel.WARNING;
selectErrorLevel(errorLevel);

// Button
ButtonElement buttonElement = $(ButtonElement.class).first();
assertHasRightClassNames(buttonElement, "v-button-error", errorLevel);

// Native button
NativeButtonElement nativeButtonElement = $(NativeButtonElement.class)
.first();
assertHasRightClassNames(nativeButtonElement, "v-nativebutton-error",
errorLevel);

// Link
LinkElement linkElement = $(LinkElement.class).first();
assertHasRightClassNames(linkElement, "v-link-error", errorLevel);

// Combo box
ComboBoxElement comboBoxElement = $(ComboBoxElement.class).get(1);
assertHasRightClassNames(comboBoxElement, "v-filterselect-error",
errorLevel);

// Date field
DateFieldElement dateFieldElement = $(DateFieldElement.class).first();
assertHasRightClassNames(dateFieldElement, "v-datefield-error",
errorLevel);

// Checkbox
CheckBoxElement checkBoxElement = $(CheckBoxElement.class).first();
assertHasRightClassNames(checkBoxElement, "v-checkbox-error",
errorLevel);

// Tab sheet
TabSheetElement tabSheetElement = $(TabSheetElement.class).first();
assertHasRightClassNames(tabSheetElement, "v-tabsheet-error",
errorLevel);

// Accordion
AccordionElement accordionElement = $(AccordionElement.class).first();
assertHasRightClassNames(accordionElement, "v-accordion-error",
errorLevel);

// Form layout
FormLayoutElement formLayoutElement = $(FormLayoutElement.class)
.first();
assertHasRightClassNames(formLayoutElement, "v-formlayout-error",
errorLevel);

// Panel
PanelElement panelElement = $(PanelElement.class).first();
assertHasRightClassNames(panelElement, "v-panel-error", errorLevel);

// Twin col select
TwinColSelectElement twinColSelectElement = $(
TwinColSelectElement.class).first();
assertHasRightClassNames(twinColSelectElement, "v-select-twincol-error",
errorLevel);
}

private void assertHasRightClassNames(WebElement element, String prefix,
ErrorLevel errorLevel) {
Assert.assertTrue("Element must have only one error level class name",
containsCorrectErrorLevelClassNameOnly(element, prefix,
errorLevel));
}

private boolean containsCorrectErrorLevelClassNameOnly(WebElement element,
String prefix, ErrorLevel errorLevel) {
List<String> classNames = new ArrayList<String>(
Arrays.asList(element.getAttribute("class").split(" ")));
classNames.retainAll(getErrorLevelClassNames(prefix,
Arrays.asList(ErrorLevel.values())));
return classNames.size() == 1 && classNames
.contains(getErrorLevelClassName(prefix, errorLevel));
}

private String getErrorLevelClassName(String prefix,
ErrorLevel errorLevel) {
return prefix + "-" + errorLevel.toString().toLowerCase();
}

private List<String> getErrorLevelClassNames(String prefix,
Collection<ErrorLevel> errorLevels) {
List<String> classNames = new ArrayList<String>(errorLevels.size());
for (ErrorLevel errorLevel : errorLevels) {
classNames.add(getErrorLevelClassName(prefix, errorLevel));
}
return classNames;
}

private void selectErrorLevel(ErrorLevel errorLevel) {
errorLevelSelector.clear();
errorLevelSelector.sendKeys(errorLevel.toString().toLowerCase());
errorLevelSelector.sendKeys(getReturn());
}

private Keys getReturn() {
if (BrowserUtil.isPhantomJS(getDesiredCapabilities())) {
return Keys.ENTER;
} else {
return Keys.RETURN;
}
}
}

+ 2
- 2
uitest/src/test/java/com/vaadin/tests/components/tabsheet/TabSheetErrorTooltipTest.java View File

@@ -84,8 +84,8 @@ public class TabSheetErrorTooltipTest extends MultiBrowserTest {
}

private WebElement getCurrentErrorMessage() {
return getDriver()
.findElement(By.xpath("//div[@class='v-errormessage']"));
return getDriver().findElement(
By.xpath("//div[contains(@class, 'v-errormessage')]"));
}

private void assertTooltip(String tooltip) {

Loading…
Cancel
Save