diff options
author | michaelvogt <michael@vaadin.com> | 2013-03-05 10:48:11 +0200 |
---|---|---|
committer | Vaadin Code Review <review@vaadin.com> | 2013-03-05 09:04:41 +0000 |
commit | b5c6f6cc0c75fa2849ad14dd395af69698440257 (patch) | |
tree | 62453df37489afee37458de72c4ec7f64b28c790 /client | |
parent | d81cd5b9c3eb4a32d4119f946706f76f305372e3 (diff) | |
download | vaadin-framework-b5c6f6cc0c75fa2849ad14dd395af69698440257.tar.gz vaadin-framework-b5c6f6cc0c75fa2849ad14dd395af69698440257.zip |
WAI-ARIA for form fields (#11180)
Changes in the base classes of the form fields for WAI-ARIA integration
Change-Id: I770082c353b1b0004875675e28f03d6a3e69f03f
Diffstat (limited to 'client')
4 files changed, 128 insertions, 1 deletions
diff --git a/client/src/com/vaadin/client/ui/AriaHelper.java b/client/src/com/vaadin/client/ui/AriaHelper.java new file mode 100644 index 0000000000..e762ba57ce --- /dev/null +++ b/client/src/com/vaadin/client/ui/AriaHelper.java @@ -0,0 +1,95 @@ +package com.vaadin.client.ui; + +import com.google.gwt.aria.client.Id; +import com.google.gwt.aria.client.InvalidValue; +import com.google.gwt.aria.client.Roles; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.ui.Widget; + +/** + * Helper class that helps to implement the WAI-ARIA functionality. + */ +public class AriaHelper { + + /** + * Binds a caption (label in HTML speak) to the form element as required by + * WAI-ARIA specification. + * + * @param widget + * Element, that should be bound to the caption + * @param captionElement + * Element of the caption + */ + public static void bindCaption(Widget widget, Element captionElement) { + assert widget != null : "Valid Widget required"; + + ensureUniqueId(captionElement); + + if (widget instanceof HandlesAriaCaption) { + ((HandlesAriaCaption) widget).handleAriaCaption(captionElement); + } else if (captionElement != null) { + String ownerId = ensureUniqueId(widget.getElement()); + captionElement.setAttribute("for", ownerId); + + Roles.getTextboxRole().setAriaLabelledbyProperty( + widget.getElement(), Id.of(captionElement)); + } else { + Roles.getTextboxRole().removeAriaLabelledbyProperty( + widget.getElement()); + } + } + + /** + * Handles the required actions depending of the input element being + * required or not. + * + * @param inputElement + * Element, typically an input element + * @param required + * boolean, true when the element is required + */ + public static void handleInputRequired(Element inputElement, + boolean required) { + if (required) { + Roles.getTextboxRole().setAriaRequiredProperty(inputElement, true); + } else { + Roles.getTextboxRole().removeAriaRequiredProperty(inputElement); + } + } + + /** + * Handles the required actions depending of the input element contains + * unaccepted input + * + * @param inputElement + * Element, typically an input element + * @param showError + * boolean, true when the element input has an error + */ + public static void handleInputError(Element inputElement, boolean showError) { + if (showError) { + Roles.getTextboxRole().setAriaInvalidState(inputElement, + InvalidValue.TRUE); + } else { + Roles.getTextboxRole().removeAriaInvalidState(inputElement); + } + } + + /** + * Makes sure that the provided element has an id attribute. Adds a new + * unique id if not. + * + * @param element + * Element to check + * @return String with the id of the element + */ + private static String ensureUniqueId(Element element) { + String id = element.getId(); + if (null == id || id.isEmpty()) { + id = DOM.createUniqueId(); + element.setId(id); + } + return id; + } +} diff --git a/client/src/com/vaadin/client/ui/HandlesAriaCaption.java b/client/src/com/vaadin/client/ui/HandlesAriaCaption.java new file mode 100644 index 0000000000..4eef0c5c25 --- /dev/null +++ b/client/src/com/vaadin/client/ui/HandlesAriaCaption.java @@ -0,0 +1,20 @@ +package com.vaadin.client.ui; + +import com.google.gwt.user.client.Element; + +/** + * Some Widgets need to handle the caption handling for WAI-ARIA themselfs, as + * for example the required ids need to be set in a specific way. In such a + * case, the Widget needs to implement this interface. + */ +public interface HandlesAriaCaption { + + /** + * Called to bind the provided caption (label in HTML speak) element to the + * main input element of the Widget. + * + * @param captionElement + * Element of the caption + */ + void handleAriaCaption(Element captionElement); +} diff --git a/client/src/com/vaadin/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java b/client/src/com/vaadin/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java index 50de8e0936..ac5a08475e 100644 --- a/client/src/com/vaadin/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java +++ b/client/src/com/vaadin/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java @@ -31,6 +31,7 @@ import com.vaadin.client.communication.StateChangeEvent.StateChangeHandler; import com.vaadin.client.ui.AbstractFieldConnector; import com.vaadin.client.ui.AbstractLayoutConnector; import com.vaadin.client.ui.LayoutClickEventHandler; +import com.vaadin.client.ui.AriaHelper; import com.vaadin.client.ui.layout.ElementResizeEvent; import com.vaadin.client.ui.layout.ElementResizeListener; import com.vaadin.shared.AbstractFieldState; @@ -258,6 +259,12 @@ public abstract class AbstractOrderedLayoutConnector extends slot.setCaption(caption, iconUrlString, styles, error, showError, required, enabled); + AriaHelper.handleInputRequired(child.getWidget().getElement(), + required); + AriaHelper.handleInputError(child.getWidget().getElement(), + showError); + AriaHelper.bindCaption(child.getWidget(), slot.getCaptionElement()); + if (slot.hasCaption()) { CaptionPosition pos = slot.getCaptionPosition(); getLayoutManager().addElementResizeListener( diff --git a/client/src/com/vaadin/client/ui/orderedlayout/Slot.java b/client/src/com/vaadin/client/ui/orderedlayout/Slot.java index 795b724292..cf19da3496 100644 --- a/client/src/com/vaadin/client/ui/orderedlayout/Slot.java +++ b/client/src/com/vaadin/client/ui/orderedlayout/Slot.java @@ -18,6 +18,7 @@ package com.vaadin.client.ui.orderedlayout; import java.util.List; +import com.google.gwt.aria.client.Roles; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.Event; @@ -92,7 +93,6 @@ public final class Slot extends SimplePanel { private ElementResizeListener spacingResizeListener; - // Caption is placed after component unless there is some part which // moves it above. private CaptionPosition captionPosition = CaptionPosition.RIGHT; @@ -479,6 +479,11 @@ public final class Slot extends SimplePanel { // character) requiredIcon.setInnerHTML("*"); requiredIcon.setClassName("v-required-field-indicator"); + + // The star should not be read by the screen reader, as it is + // purely visual. Required state is set at the element level for + // the screen reader. + Roles.getTextboxRole().setAriaHiddenState(requiredIcon, true); } caption.appendChild(requiredIcon); } else if (requiredIcon != null) { |