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();
+ }
+
+}