TAP5-2075 - Checkbox Checked and Unchecked validator with client side validation
authorBalázs Palcsó <palcso.balazs@gmail.com>
Sat, 29 Dec 2018 14:58:14 +0000 (15:58 +0100)
committerBalázs Palcsó <palcso.balazs@gmail.com>
Thu, 17 Jan 2019 17:33:18 +0000 (18:33 +0100)
16 files changed:
tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/fields.coffee
tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/utils.coffee
tapestry-core/src/main/coffeescript/META-INF/modules/t5/core/validation.coffee
tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Checkbox.java
tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java
tapestry-core/src/main/java/org/apache/tapestry5/validator/CheckboxValidator.java [new file with mode: 0644]
tapestry-core/src/main/java/org/apache/tapestry5/validator/Checked.java [new file with mode: 0644]
tapestry-core/src/main/java/org/apache/tapestry5/validator/Unchecked.java [new file with mode: 0644]
tapestry-core/src/main/resources/org/apache/tapestry5/core.properties
tapestry-core/src/main/resources/org/apache/tapestry5/core_hu.properties
tapestry-core/src/test/app1/ValidateCheckboxMustBeChecked.tml [new file with mode: 0644]
tapestry-core/src/test/app1/ValidateCheckboxMustBeUnchecked.tml [new file with mode: 0644]
tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/FormTests.java
tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/ValidateCheckboxMustBeChecked.java [new file with mode: 0644]
tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/ValidateCheckboxMustBeUnchecked.java [new file with mode: 0644]

index 6ae7fcb..272be71 100644 (file)
@@ -110,6 +110,8 @@ define ["underscore", "./events", "./dom", "./utils", "./forms"],
       fieldValue =
         if (@attr "data-value-mode") is "options"
           collectOptionValues this
+        else if @element.type is "checkbox"
+          @checked()
         else
           @value()
 
index 5829ea0..c89b294 100644 (file)
@@ -50,6 +50,8 @@ define ["underscore"],
 
           if _.isArray input
             return input.length is 0
+            
+          return false if typeof input is "boolean"
 
           return (exports.trim input).length is 0
 
index b44f043..7269bb8 100644 (file)
@@ -164,5 +164,15 @@ define ["underscore", "./dom", "./events", "./utils", "./messages", "./fields"],
         memo.error = (@attr "data-regexp-message") or "INVALID"
         return false
 
+    dom.onDocument events.field.validate, "[data-expected-status=checked]", (event, memo) ->
+
+      unless memo.value
+        memo.error =  (@attr "data-checked-message") or "MUST BE CHECKED"
+
+    dom.onDocument events.field.validate, "[data-expected-status=unchecked]", (event, memo) ->
+
+      if memo.value
+        memo.error =  (@attr "data-checked-message") or "MUST NOT BE CHECKED"
+
     # Export the number parser, just to be nice (and to support some testing).
     return { parseNumber }
\ No newline at end of file
index d8654f7..1492ff9 100644 (file)
 
 package org.apache.tapestry5.corelib.components;
 
-import org.apache.tapestry5.Binding;
-import org.apache.tapestry5.BindingConstants;
-import org.apache.tapestry5.FieldValidator;
-import org.apache.tapestry5.MarkupWriter;
-import org.apache.tapestry5.ValidationException;
+import org.apache.tapestry5.*;
 import org.apache.tapestry5.annotations.AfterRender;
 import org.apache.tapestry5.annotations.BeginRender;
 import org.apache.tapestry5.annotations.Mixin;
@@ -47,7 +43,7 @@ public class Checkbox extends AbstractField
     @Parameter(defaultPrefix = BindingConstants.VALIDATE, allowNull = false)
     @SuppressWarnings("unchecked")
     private FieldValidator<Object> validate;
-    
+
     @SuppressWarnings("unused")
     @Mixin
     private RenderDisabled renderDisabled;
@@ -63,11 +59,16 @@ public class Checkbox extends AbstractField
                 "name", getControlName(),
                 "id", getClientId(),
                 "checked", checked ? "checked" : null);
-        
+
+        putPropertyNameIntoBeanValidationContext("value");
+
         validate.render(writer);
 
+        removePropertyNameFromBeanValidationContext();
+
         resources.renderInformalParameters(writer);
 
+
         decorateInsideField();
     }
 
@@ -83,21 +84,23 @@ public class Checkbox extends AbstractField
         String postedValue = request.getParameter(controlName);
 
         boolean translated = postedValue != null;
-        
+
         // record as "true" or "false"
         validationTracker.recordInput(this, Boolean.toString(translated));
-        
+
+        putPropertyNameIntoBeanValidationContext("value");
         try
         {
             fieldValidationSupport.validate(translated, resources, validate);
-            
+
             value = translated;
         } catch (ValidationException ex)
         {
             validationTracker.recordError(this, ex.getMessage());
         }
+        removePropertyNameFromBeanValidationContext();
     }
-    
+
     /**
      * Computes a default value for the "validate" parameter using
      * {@link org.apache.tapestry5.services.FieldValidatorDefaultSource}.
index 7529edc..52fdf72 100644 (file)
@@ -694,6 +694,8 @@ public final class TapestryModule
         configuration.addInstance("max", Max.class);
         configuration.addInstance("regexp", Regexp.class);
         configuration.addInstance("email", Email.class);
+        configuration.addInstance("checked", Checked.class);
+        configuration.addInstance("unchecked", Unchecked.class);
         configuration.add("none", new None());
     }
 
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/validator/CheckboxValidator.java b/tapestry-core/src/main/java/org/apache/tapestry5/validator/CheckboxValidator.java
new file mode 100644 (file)
index 0000000..9ae7af0
--- /dev/null
@@ -0,0 +1,84 @@
+// Copyright 2018 The Apache Software Foundation
+//
+// 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 org.apache.tapestry5.validator;
+
+import org.apache.tapestry5.Field;
+import org.apache.tapestry5.MarkupWriter;
+import org.apache.tapestry5.ValidationException;
+import org.apache.tapestry5.ioc.MessageFormatter;
+import org.apache.tapestry5.services.FormSupport;
+import org.apache.tapestry5.services.javascript.DataConstants;
+import org.apache.tapestry5.services.javascript.JavaScriptSupport;
+
+/**
+ * A validator that enforces that the value is true or false. This validator is not configurable.
+ *
+ * @since 5.5.0
+ */
+class CheckboxValidator extends AbstractValidator<Void, Object>
+{
+    private final Boolean expectedValue;
+    private final String expectedStatus;
+
+    public CheckboxValidator(JavaScriptSupport javaScriptSupport, String messageKey, Boolean expectedValue, String expectedStatus)
+    {
+        super(null, Object.class, messageKey, javaScriptSupport);
+        this.expectedValue = expectedValue;
+        this.expectedStatus = expectedStatus;
+    }
+
+    @Override
+    public void validate(Field field, Void constraintValue, MessageFormatter formatter, Object value)
+            throws ValidationException
+    {
+        if (!expectedValue.equals(value))
+            throw new ValidationException(buildMessage(formatter, field));
+    }
+
+    private String buildMessage(MessageFormatter formatter, Field field)
+    {
+        return formatter.format(field.getLabel());
+    }
+
+    /**
+     * The exception to the rule.
+     */
+    @Override
+    public boolean isRequired()
+    {
+        return true;
+    }
+
+    @Override
+    public void render(Field field, Void constraintValue, MessageFormatter formatter, MarkupWriter writer,
+                       FormSupport formSupport)
+    {
+        if (formSupport.isClientValidationEnabled())
+        {
+            javaScriptSupport.require("t5/core/validation");
+
+            writer.attributes(
+                    DataConstants.VALIDATION_ATTRIBUTE, true,
+                    "data-expected-status", expectedStatus,
+                    "data-checked-message", buildMessage(formatter, field));
+        }
+    }
+
+    @Override
+    public String toString()
+    {
+        return "Checkbox must be " + expectedStatus + " validator";
+    }
+}
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/validator/Checked.java b/tapestry-core/src/main/java/org/apache/tapestry5/validator/Checked.java
new file mode 100644 (file)
index 0000000..6a24cc9
--- /dev/null
@@ -0,0 +1,49 @@
+// Copyright 2018 The Apache Software Foundation
+//
+// 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 org.apache.tapestry5.validator;
+
+import org.apache.tapestry5.Field;
+import org.apache.tapestry5.MarkupWriter;
+import org.apache.tapestry5.ioc.MessageFormatter;
+import org.apache.tapestry5.services.FormSupport;
+import org.apache.tapestry5.services.Html5Support;
+import org.apache.tapestry5.services.javascript.JavaScriptSupport;
+
+/**
+ * A validator that enforces that the value is true. This validator is not configurable.
+ * 
+ * @since 5.5.0
+ */
+public final class Checked extends CheckboxValidator
+{
+    final private Html5Support html5Support;
+    
+    public Checked(JavaScriptSupport javaScriptSupport, Html5Support html5Support)
+    {
+        super(javaScriptSupport, "checked", Boolean.TRUE, "checked");
+        this.html5Support = html5Support;
+    }
+
+    public void render(Field field, Void constraintValue, MessageFormatter formatter, MarkupWriter writer,
+                       FormSupport formSupport)
+    {
+        super.render(field, constraintValue, formatter, writer, formSupport);
+
+        if (html5Support.isHtml5SupportEnabled())
+        {
+            writer.attributes("required", "required");
+        }
+    }
+}
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/validator/Unchecked.java b/tapestry-core/src/main/java/org/apache/tapestry5/validator/Unchecked.java
new file mode 100644 (file)
index 0000000..e358ee3
--- /dev/null
@@ -0,0 +1,32 @@
+// Copyright 2018 The Apache Software Foundation
+//
+// 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 org.apache.tapestry5.validator;
+
+import org.apache.tapestry5.services.Html5Support;
+import org.apache.tapestry5.services.javascript.JavaScriptSupport;
+
+/**
+ * A validator that enforces that the value is false. This validator is not configurable.
+ * 
+ * @since 5.5.0
+ */
+public final class Unchecked extends CheckboxValidator
+{
+    public Unchecked(JavaScriptSupport javaScriptSupport, Html5Support html5Support)
+    {
+        super(javaScriptSupport, "unchecked", Boolean.FALSE, "unchecked");
+    }
+
+}
index 17281ca..3c04f2c 100644 (file)
@@ -95,6 +95,12 @@ regexp=%2$s does not match pattern '%1$s'.
 # "required" validator error:
 required=You must provide a value for %s.
 
+# "checked" validator error:
+checked=You must check %s.
+
+# "unchecked" validator error:
+unchecked=You must uncheck %s.
+
 # Client-side numeric validation error:
 core-input-not-numeric=Value is not numeric.
 
index 5711e98..fb96493 100644 (file)
@@ -95,6 +95,12 @@ regexp=%2$s nem felel meg az elvárt '%1$s' formátumnak.
 # "required" validator error:
 required=%s megadása kötelező.
 
+# "checked" validator error:
+checked=%s bepipálása kötelező.
+
+# "unchecked" validator error:
+unchecked=%s bepipálása nem megengedett.
+
 # Client-side numeric validation error:
 core-input-not-numeric=A megadott érték nem szám.
 
diff --git a/tapestry-core/src/test/app1/ValidateCheckboxMustBeChecked.tml b/tapestry-core/src/test/app1/ValidateCheckboxMustBeChecked.tml
new file mode 100644 (file)
index 0000000..4f8315e
--- /dev/null
@@ -0,0 +1,7 @@
+<html t:type="border" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+<t:form>
+    <t:Checkbox value="value" validate="checked" />
+    <t:submit/>
+</t:form>
+Checkbox's value: ${value}
+</html>
diff --git a/tapestry-core/src/test/app1/ValidateCheckboxMustBeUnchecked.tml b/tapestry-core/src/test/app1/ValidateCheckboxMustBeUnchecked.tml
new file mode 100644 (file)
index 0000000..ae7b639
--- /dev/null
@@ -0,0 +1,7 @@
+<html t:type="border" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+<t:form>
+    <t:Checkbox value="value" validate="unchecked" />
+    <t:submit/>
+</t:form>
+Checkbox's value: ${value}
+</html>
index 5db2f79..ad7a3f6 100644 (file)
@@ -1266,6 +1266,42 @@ public class FormTests extends App1TestCase
         assertTextPresent("Validate in error");
     }
 
+    /** TAP5-2075 **/
+    @Test
+    public void validate_checkbox_must_be_checked()
+    {
+        openLinks("Validate Checkbox Must Be Checked");
+
+        clickAndWait(SUBMIT);
+
+        assertTextPresent("You must check Checkbox.");
+
+        check("//input[@type='checkbox']");
+
+        clickAndWait(SUBMIT);
+
+        assertTextPresent("Checkbox's value: true");
+    }
+
+    /** TAP5-2075 **/
+    @Test
+    public void validate_checkbox_must_be_unchecked()
+    {
+        openLinks("Validate Checkbox Must Be Unchecked");
+
+        check("//input[@type='checkbox']");
+
+        clickAndWait(SUBMIT);
+
+        assertTextPresent("You must uncheck Checkbox.");
+
+        uncheck("//input[@type='checkbox']");
+
+        clickAndWait(SUBMIT);
+
+        assertTextPresent("Checkbox's value: false");
+    }
+
     // TAP5-2204
     @Test
     public void select_model_with_auto_security_and_non_persistent_model() throws Exception
index df579be..2c87ae7 100644 (file)
@@ -601,6 +601,12 @@ public class Index
 
                     new Item("textfieldwithnullvalidateparameter", "TextField with null validate parameter", "A TextField whose validate parameter is bound to null"),
 
+                    new Item("validateCheckboxMustBeChecked", "Validate Checkbox Must Be Checked", "A form that trigger validate in " +
+                            "error event on submit when checkbox is not checked"),
+
+                    new Item("validateCheckboxMustBeUnchecked", "Validate Checkbox Must Be Unchecked", "A form that trigger validate in " +
+                               "error event on submit when checkbox is checked"),
+                    
                     new Item("validateInErrorEvent", "Validate in error Event", "A form that trigger validate in " +
                             "error event on submit when textfield is empty"),
 
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/ValidateCheckboxMustBeChecked.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/ValidateCheckboxMustBeChecked.java
new file mode 100644 (file)
index 0000000..85ef23e
--- /dev/null
@@ -0,0 +1,25 @@
+// Copyright 2018 The Apache Software Foundation
+//
+// 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 org.apache.tapestry5.integration.app1.pages;
+
+import org.apache.tapestry5.annotations.Persist;
+import org.apache.tapestry5.annotations.Property;
+
+public class ValidateCheckboxMustBeChecked {
+    
+       @Property
+       @Persist
+       private boolean value;
+}
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/ValidateCheckboxMustBeUnchecked.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/ValidateCheckboxMustBeUnchecked.java
new file mode 100644 (file)
index 0000000..d8c28ef
--- /dev/null
@@ -0,0 +1,25 @@
+// Copyright 2018 The Apache Software Foundation
+//
+// 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 org.apache.tapestry5.integration.app1.pages;
+
+import org.apache.tapestry5.annotations.Persist;
+import org.apache.tapestry5.annotations.Property;
+
+public class ValidateCheckboxMustBeUnchecked {
+    
+       @Property
+       @Persist
+       private boolean value;
+}