You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by th...@apache.org on 2020/07/04 19:24:35 UTC

[tapestry-5] 01/02: TAP5-2631: Make forms more accessible with WAI-ARIA attributes

This is an automated email from the ASF dual-hosted git repository.

thiagohp pushed a commit to branch java9modules
in repository https://gitbox.apache.org/repos/asf/tapestry-5.git

commit 2d89f1f69931bb1d76a48f69f6bd9133f5b10714
Author: Thiago H. de Paula Figueiredo <th...@arsmachina.com.br>
AuthorDate: Sat Jul 4 16:21:48 2020 -0300

    TAP5-2631: Make forms more accessible with WAI-ARIA attributes
---
 .../META-INF/modules/t5/core/fields.coffee         | 11 +++++++--
 .../apache/tapestry5/corelib/components/Error.java | 11 +++++++--
 .../tapestry5/corelib/components/Errors.java       |  2 +-
 .../apache/tapestry5/corelib/components/Label.java | 27 +++++++++++++++++++++-
 .../org/apache/tapestry5/validator/Required.java   |  3 ++-
 5 files changed, 47 insertions(+), 7 deletions(-)

diff --git a/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/fields.coffee b/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/fields.coffee
index 272be71..5cbf72a 100644
--- a/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/fields.coffee
+++ b/tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/fields.coffee
@@ -80,6 +80,7 @@ define ["underscore", "./events", "./dom", "./utils", "./forms"],
       block = dom.create "p",
         class: "help-block"
         "data-error-block-for": fieldId
+        "id": fieldId + "-help-block"
 
       # The .input-group selectors are used to attach buttons or markers to the field.
       # In which case, the help block can go after the group instead.
@@ -100,7 +101,7 @@ define ["underscore", "./events", "./dom", "./utils", "./forms"],
 
     dom.onDocument events.field.inputValidation, (event, formMemo) ->
 
-      # Fields that are disbled, or not visible to the user are not subject to
+      # Fields that are disabled, or not visible to the user are not subject to
       # validation. Typically, a field will only be invisible due to the
       # core/FormFragment component.
       return if @element.disabled or (not @deepVisible())
@@ -116,7 +117,7 @@ define ["underscore", "./events", "./dom", "./utils", "./forms"],
           @value()
 
       memo = value: fieldValue
-
+      
       postEventTrigger = =>
         if memo.error
           # Assume the event handler displayed the message.
@@ -146,7 +147,11 @@ define ["underscore", "./events", "./dom", "./utils", "./forms"],
 
       if failure
         formMemo.error = true
+        this.attr('aria-invalid', 'true');
+        this.attr('aria-describedby', this.attr('id') + "-help-block");
       else
+        this.attr('aria-invalid', 'false');
+        this.attr('aria-describedby ', null);
         @trigger events.field.clearValidationError
 
       return
@@ -157,6 +162,7 @@ define ["underscore", "./events", "./dom", "./utils", "./forms"],
       for block in blocks or []
         block.hide().update("")
         block.parent().removeClass "has-error"
+        block.attr("role", null)
 
       group = @findParent ".form-group"
 
@@ -176,6 +182,7 @@ define ["underscore", "./events", "./dom", "./utils", "./forms"],
         # where the help block can't be under the same .form-group element as the field (more common
         # with a horizontal form layout).
         block.parent().addClass("has-error")
+        block.attr("role", "alert")
 
       group = @findParent ".form-group"
 
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Error.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Error.java
index 3f0119a..9d3f7d9 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Error.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Error.java
@@ -63,8 +63,9 @@ public class Error
     {
         // Initially invisible; will be shown on client if an error exists.
         Element element = writer.element("p", "class", 
-                !("help-block".equals(cssClass)) ? ("help-block " + cssClass) : cssClass + " invisible");
-
+                !("help-block".equals(cssClass)) ? ("help-block " + cssClass) : cssClass + " invisible",
+                        "role", "alert");
+        
         resources.renderInformalParameters(writer);
 
         // Wait until the end of the heartbeat to ensure the Field has had a chance to render.
@@ -80,5 +81,11 @@ public class Error
     {
         // The field may add an id attribute because of this call.
         element.attribute("data-error-block-for", field.getClientId());
+        String id = field.getClientId() + "-help-block";
+        element.attribute("id", id);
+        Element input = element.getDocument().getElementById(field.getClientId());
+        if (input != null) {
+            input.attribute("aria-describedby", id);
+        }
     }
 }
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Errors.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Errors.java
index d45dfbf..768d988 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Errors.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Errors.java
@@ -103,7 +103,7 @@ public class Errors
 
         Set<String> previousErrors = CollectionFactory.newSet();
 
-        writer.element("div", "class", baseCssClass + " " + className);
+        writer.element("div", "class", baseCssClass + " " + className, "role", "alert");
         writer.element("button",
                 "type", "button",
                 "class", closeButtonCssClass,
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Label.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Label.java
index 815200a..bc3fa8d 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Label.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Label.java
@@ -43,6 +43,19 @@ public class Label
     @Parameter(name = "for", required = true, allowNull = false, defaultPrefix = BindingConstants.COMPONENT)
     private Field field;
 
+    /**
+     * Used to explicitly set the client-side id of the element for this component. Normally this is not
+     * bound (or null) and {@link org.apache.tapestry5.services.javascript.JavaScriptSupport#allocateClientId(org.apache.tapestry5.ComponentResources)}
+     * is used to generate a unique client-id based on the component's id. In some cases, when creating client-side
+     * behaviors, it is useful to explicitly set a unique id for an element using this parameter.
+     * 
+     * Certain values, such as "submit", "method", "reset", etc., will cause client-side conflicts and are not allowed; using such will
+     * cause a runtime exception.
+     * @since 5.6.0
+     */
+    @Parameter(defaultPrefix = BindingConstants.LITERAL)
+    private String clientId;
+
     @Environmental
     private ValidationDecorator decorator;
 
@@ -67,6 +80,10 @@ public class Label
 
     private Element labelElement;
 
+    private String string;
+
+    private String string2;
+
     boolean beginRender(MarkupWriter writer)
     {
         decorator.beforeLabel(field);
@@ -96,8 +113,16 @@ public class Label
               + " is linked to a Field that failed to return a clientId. The 'for' attibute will not be rendered.";
             javaScriptSupport.require("t5/core/console").invoke("warn").with(warningText);
         }
-
+        
+        String id = clientId != null ? clientId : javaScriptSupport.allocateClientId(fieldId + "-label");
+        labelElement.attribute("id", id);
         labelElement.forceAttributes("for", fieldId);
+        
+        Element input = labelElement.getDocument().getElementById(field.getClientId());
+        if (input != null) {
+            input.attribute("aria-labelledby", id);
+        }
+        
         decorator.insideLabel(field, labelElement);
     }
 
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/validator/Required.java b/tapestry-core/src/main/java/org/apache/tapestry5/validator/Required.java
index 8e17948..ee76388 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/validator/Required.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/validator/Required.java
@@ -68,7 +68,8 @@ public final class Required extends AbstractValidator<Void, Object>
             writer.attributes(
                     DataConstants.VALIDATION_ATTRIBUTE, true,
                     "data-optionality", "required",
-                    "data-required-message", buildMessage(formatter, field));
+                    "data-required-message", buildMessage(formatter, field),
+                    "aria-required", "true");
         }
         if (html5Support.isHtml5SupportEnabled())
         {