You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2008/08/21 19:32:19 UTC

svn commit: r687809 - in /tapestry/tapestry5/trunk/tapestry-core/src: main/java/org/apache/tapestry5/ main/java/org/apache/tapestry5/corelib/components/ main/java/org/apache/tapestry5/internal/services/ main/java/org/apache/tapestry5/internal/util/ mai...

Author: hlship
Date: Thu Aug 21 10:32:17 2008
New Revision: 687809

URL: http://svn.apache.org/viewvc?rev=687809&view=rev
Log:
TAPESTRY-2357: Unlike Tapestry 4, Tapestry 5 does not automatically position the cursor into a form

Added:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/FieldFocusPriority.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/AutofocusValidationDecorator.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/ValidationDecoratorWrapper.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/util/AutofocusValidationDecoratorTest.java
Modified:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/RenderSupport.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Form.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RenderSupportImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tapestry.js
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/RenderSupportImplTest.java

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/FieldFocusPriority.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/FieldFocusPriority.java?rev=687809&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/FieldFocusPriority.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/FieldFocusPriority.java Thu Aug 21 10:32:17 2008
@@ -0,0 +1,36 @@
+//  Copyright 2008 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;
+
+/**
+ * Used to determine which field on a page should receive focus, based on its status.
+ */
+public enum FieldFocusPriority
+{
+    /**
+     * An optional field, the lowest priority.
+     */
+    OPTIONAL,
+
+    /**
+     * A field whose input is required, which takes higher priority than optional.
+     */
+    REQUIRED,
+
+    /**
+     * A field that contains a validation error, the highest priority.
+     */
+    IN_ERROR
+}

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/RenderSupport.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/RenderSupport.java?rev=687809&r1=687808&r2=687809&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/RenderSupport.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/RenderSupport.java Thu Aug 21 10:32:17 2008
@@ -119,4 +119,13 @@
      * @param parameters
      */
     void addInit(String functionName, String... parameters);
+
+    /**
+     * Invoked to set focus on a rendered field. Takes into account priority, meaning that a field with errors will take
+     * precendence over a merely required field, and over a field that is optional.
+     *
+     * @param priority focus is set only if the provided priority is greater than the current priority
+     * @param fieldId  id of client-side element to take focus
+     */
+    void autofocus(FieldFocusPriority priority, String fieldId);
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Form.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Form.java?rev=687809&r1=687808&r2=687809&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Form.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Form.java Thu Aug 21 10:32:17 2008
@@ -24,6 +24,7 @@
 import org.apache.tapestry5.internal.services.ComponentInvocationMap;
 import org.apache.tapestry5.internal.services.ComponentResultProcessorWrapper;
 import org.apache.tapestry5.internal.services.HeartbeatImpl;
+import org.apache.tapestry5.internal.util.AutofocusValidationDecorator;
 import org.apache.tapestry5.internal.util.Base64ObjectInputStream;
 import org.apache.tapestry5.ioc.Location;
 import org.apache.tapestry5.ioc.Messages;
@@ -135,6 +136,13 @@
     @Parameter(defaultPrefix = BindingConstants.LITERAL)
     private String zone;
 
+    /**
+     * If true (the default), then the JavaScript will be added to position the cursor into the form. The field to
+     * receive focus is the first rendered field that is in error, or required, or present (in that order of priority).
+     */
+    @Parameter
+    private boolean autofocus = true;
+
     @Inject
     private Logger logger;
 
@@ -217,6 +225,13 @@
         environment.push(FormSupport.class, formSupport);
         environment.push(ValidationTracker.class, tracker);
 
+        if (autofocus)
+        {
+            ValidationDecorator autofocusDecorator = new AutofocusValidationDecorator(environment.peek(
+                    ValidationDecorator.class), tracker, renderSupport);
+            environment.push(ValidationDecorator.class, autofocusDecorator);
+        }
+
         // Now that the environment is setup, inform the component or other listeners that the form
         // is about to render.  
 
@@ -275,6 +290,9 @@
                     "type", "hidden",
                     "name", FORM_DATA,
                     "value", actionSink.toBase64());
+
+        if (autofocus)
+            environment.pop(ValidationDecorator.class);
     }
 
     void cleanupRender()

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RenderSupportImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RenderSupportImpl.java?rev=687809&r1=687808&r2=687809&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RenderSupportImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RenderSupportImpl.java Thu Aug 21 10:32:17 2008
@@ -16,6 +16,7 @@
 
 import org.apache.tapestry5.Asset;
 import org.apache.tapestry5.ComponentResources;
+import org.apache.tapestry5.FieldFocusPriority;
 import org.apache.tapestry5.RenderSupport;
 import org.apache.tapestry5.ioc.internal.util.Defense;
 import static org.apache.tapestry5.ioc.internal.util.Defense.notNull;
@@ -45,6 +46,10 @@
 
     private final JSONObject init = new JSONObject();
 
+    private FieldFocusPriority focusPriority;
+
+    private String focusFieldId;
+
     /**
      * @param linker       Used to assemble JavaScript includes and snippets
      * @param symbolSource Used to example symbols (in {@linkplain #addClasspathScriptLink(String...) in classpath
@@ -164,6 +169,18 @@
         addInitFunctionInvocation(functionName, array);
     }
 
+    public void autofocus(FieldFocusPriority priority, String fieldId)
+    {
+        Defense.notNull(priority, "priority");
+        Defense.notBlank(fieldId, "fieldId");
+
+        if (focusFieldId == null || priority.compareTo(focusPriority) > 0)
+        {
+            this.focusPriority = priority;
+            focusFieldId = fieldId;
+        }
+    }
+
     private void addInitFunctionInvocation(String functionName, Object parameters)
     {
         Defense.notBlank(functionName, "functionName");
@@ -185,6 +202,11 @@
      */
     public void commit()
     {
+        if (focusFieldId != null)
+        {
+            addScript("Tapestry.focus('%s');", focusFieldId);
+        }
+
         if (init.length() > 0)
         {
             addScript("Tapestry.init(%s);", init);

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/AutofocusValidationDecorator.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/AutofocusValidationDecorator.java?rev=687809&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/AutofocusValidationDecorator.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/AutofocusValidationDecorator.java Thu Aug 21 10:32:17 2008
@@ -0,0 +1,56 @@
+//  Copyright 2008 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.internal.util;
+
+import org.apache.tapestry5.*;
+
+/**
+ * Used by {@link org.apache.tapestry5.corelib.components.Form} to determine which fields will be focused and a what
+ * priority.
+ */
+public class AutofocusValidationDecorator extends ValidationDecoratorWrapper
+{
+    private final ValidationTracker tracker;
+
+    private final RenderSupport renderSupport;
+
+    public AutofocusValidationDecorator(ValidationDecorator delegate, ValidationTracker tracker,
+                                        RenderSupport renderSupport)
+    {
+        super(delegate);
+        this.tracker = tracker;
+        this.renderSupport = renderSupport;
+    }
+
+    @Override
+    public void insideField(Field field)
+    {
+        super.insideField(field);
+
+        if (!field.isDisabled())
+        {
+            renderSupport.autofocus(getPriority(field), field.getClientId());
+        }
+    }
+
+    private FieldFocusPriority getPriority(Field field)
+    {
+        if (tracker.inError(field)) return FieldFocusPriority.IN_ERROR;
+
+        if (field.isRequired()) return FieldFocusPriority.REQUIRED;
+
+        return FieldFocusPriority.OPTIONAL;
+    }
+}

Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/ValidationDecoratorWrapper.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/ValidationDecoratorWrapper.java?rev=687809&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/ValidationDecoratorWrapper.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/ValidationDecoratorWrapper.java Thu Aug 21 10:32:17 2008
@@ -0,0 +1,63 @@
+//  Copyright 2008 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.internal.util;
+
+import org.apache.tapestry5.Field;
+import org.apache.tapestry5.ValidationDecorator;
+import org.apache.tapestry5.dom.Element;
+
+/**
+ * Implementation of {@link org.apache.tapestry5.ValidationDecorator} that delegates all method invocations. Subclasses
+ * may override any of the methods.
+ */
+public class ValidationDecoratorWrapper implements ValidationDecorator
+{
+    private final ValidationDecorator delegate;
+
+    public ValidationDecoratorWrapper(ValidationDecorator delegate)
+    {
+        this.delegate = delegate;
+    }
+
+    public void beforeLabel(Field field)
+    {
+        delegate.beforeLabel(field);
+    }
+
+    public void insideLabel(Field field, Element labelElement)
+    {
+        delegate.insideLabel(field, labelElement);
+    }
+
+    public void afterLabel(Field field)
+    {
+        delegate.afterLabel(field);
+    }
+
+    public void beforeField(Field field)
+    {
+        delegate.beforeField(field);
+    }
+
+    public void insideField(Field field)
+    {
+        delegate.insideField(field);
+    }
+
+    public void afterField(Field field)
+    {
+        delegate.afterField(field);
+    }
+}

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tapestry.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tapestry.js?rev=687809&r1=687808&r2=687809&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tapestry.js (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tapestry.js Thu Aug 21 10:32:17 2008
@@ -257,6 +257,16 @@
         return true;
     },
 
+    /** Focuses on a field, selecting its text. */
+    focus : function (field)
+    {
+        field = $(field);
+
+        if (field.focus) field.focus();
+
+        if (field.select) field.select();
+    },
+
     /**
      * Default function for handling Ajax-related failures.
      */
@@ -674,7 +684,7 @@
 
             this.outerDiv.hide();
 
-            this.field.focus();
+            Tapestry.focus(this.field);
 
             Event.stop(event);  // Should be domevent.stop(), but that fails under IE
         }.bindAsEventListener(this));

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/RenderSupportImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/RenderSupportImplTest.java?rev=687809&r1=687808&r2=687809&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/RenderSupportImplTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/RenderSupportImplTest.java Thu Aug 21 10:32:17 2008
@@ -15,6 +15,7 @@
 package org.apache.tapestry5.internal.services;
 
 import org.apache.tapestry5.Asset;
+import org.apache.tapestry5.FieldFocusPriority;
 import org.apache.tapestry5.RenderSupport;
 import org.apache.tapestry5.internal.test.InternalBaseTestCase;
 import org.apache.tapestry5.ioc.services.SymbolSource;
@@ -197,4 +198,60 @@
 
         verify();
     }
+
+    @Test
+    public void field_focus()
+    {
+        DocumentLinker linker = mockDocumentLinker();
+
+        linker.addScript("Tapestry.focus('foo');");
+
+        replay();
+
+        RenderSupportImpl support = new RenderSupportImpl(linker, null, null);
+
+        support.autofocus(FieldFocusPriority.OPTIONAL, "foo");
+
+        support.commit();
+
+        verify();
+    }
+
+    @Test
+    public void first_focus_field_at_priority_wins()
+    {
+        DocumentLinker linker = mockDocumentLinker();
+
+        linker.addScript("Tapestry.focus('foo');");
+
+        replay();
+
+        RenderSupportImpl support = new RenderSupportImpl(linker, null, null);
+
+        support.autofocus(FieldFocusPriority.OPTIONAL, "foo");
+        support.autofocus(FieldFocusPriority.OPTIONAL, "bar");
+
+        support.commit();
+
+        verify();
+    }
+
+    @Test
+    public void higher_priority_wins_focus()
+    {
+        DocumentLinker linker = mockDocumentLinker();
+
+        linker.addScript("Tapestry.focus('bar');");
+
+        replay();
+
+        RenderSupportImpl support = new RenderSupportImpl(linker, null, null);
+
+        support.autofocus(FieldFocusPriority.OPTIONAL, "foo");
+        support.autofocus(FieldFocusPriority.REQUIRED, "bar");
+
+        support.commit();
+
+        verify();
+    }
 }

Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/util/AutofocusValidationDecoratorTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/util/AutofocusValidationDecoratorTest.java?rev=687809&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/util/AutofocusValidationDecoratorTest.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/util/AutofocusValidationDecoratorTest.java Thu Aug 21 10:32:17 2008
@@ -0,0 +1,126 @@
+//  Copyright 2008 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.internal.util;
+
+import org.apache.tapestry5.*;
+import org.apache.tapestry5.test.TapestryTestCase;
+import org.testng.annotations.Test;
+
+public class AutofocusValidationDecoratorTest extends TapestryTestCase
+{
+    @Test
+    public void field_is_disabled()
+    {
+        Field field = mockField();
+        ValidationDecorator delegate = mockValidationDecorator();
+        ValidationTracker tracker = mockValidationTracker();
+        RenderSupport renderSupport = mockRenderSupport();
+
+        delegate.insideField(field);
+
+        train_isDisabled(field, true);
+
+        replay();
+
+        ValidationDecorator decorator = new AutofocusValidationDecorator(delegate, tracker, renderSupport);
+
+        decorator.insideField(field);
+
+        verify();
+    }
+
+    @Test
+    public void field_is_in_error()
+    {
+        Field field = mockField();
+        ValidationDecorator delegate = mockValidationDecorator();
+        ValidationTracker tracker = mockValidationTracker();
+        RenderSupport renderSupport = mockRenderSupport();
+
+        delegate.insideField(field);
+
+        train_isDisabled(field, false);
+        train_inError(tracker, field, true);
+
+        train_getClientId(field, "foo");
+
+        renderSupport.autofocus(FieldFocusPriority.IN_ERROR, "foo");
+
+        replay();
+
+        ValidationDecorator decorator = new AutofocusValidationDecorator(delegate, tracker, renderSupport);
+
+        decorator.insideField(field);
+
+        verify();
+    }
+
+    @Test
+    public void field_is_required()
+    {
+        Field field = mockField();
+        ValidationDecorator delegate = mockValidationDecorator();
+        ValidationTracker tracker = mockValidationTracker();
+        RenderSupport renderSupport = mockRenderSupport();
+
+        delegate.insideField(field);
+
+        train_isDisabled(field, false);
+        train_inError(tracker, field, false);
+
+        train_isRequired(field, true);
+
+        train_getClientId(field, "foo");
+
+        renderSupport.autofocus(FieldFocusPriority.REQUIRED, "foo");
+
+        replay();
+
+        ValidationDecorator decorator = new AutofocusValidationDecorator(delegate, tracker, renderSupport);
+
+        decorator.insideField(field);
+
+        verify();
+    }
+
+    @Test
+    public void field_is_optional()
+    {
+        Field field = mockField();
+        ValidationDecorator delegate = mockValidationDecorator();
+        ValidationTracker tracker = mockValidationTracker();
+        RenderSupport renderSupport = mockRenderSupport();
+
+        delegate.insideField(field);
+
+        train_isDisabled(field, false);
+        train_inError(tracker, field, false);
+
+        train_isRequired(field, false);
+
+        train_getClientId(field, "foo");
+
+        renderSupport.autofocus(FieldFocusPriority.OPTIONAL, "foo");
+
+        replay();
+
+        ValidationDecorator decorator = new AutofocusValidationDecorator(delegate, tracker, renderSupport);
+
+        decorator.insideField(field);
+
+        verify();
+    }
+
+}