[Checkins] SVN: z3c.formjs/trunk/src/z3c/formjs/ Wrote tests for jsswitch and improved the API a little bit. Also

Stephan Richter srichter at cosmos.phy.tufts.edu
Thu Aug 23 15:08:29 EDT 2007


Log message for revision 79169:
  Wrote tests for jsswitch and improved the API a little bit. Also 
  ensured that the PT engines get cleaned up after the tests.
  

Changed:
  U   z3c.formjs/trunk/src/z3c/formjs/jsswitch.py
  A   z3c.formjs/trunk/src/z3c/formjs/jsswitch.txt
  U   z3c.formjs/trunk/src/z3c/formjs/testing.py
  U   z3c.formjs/trunk/src/z3c/formjs/tests/test_doc.py

-=-
Modified: z3c.formjs/trunk/src/z3c/formjs/jsswitch.py
===================================================================
--- z3c.formjs/trunk/src/z3c/formjs/jsswitch.py	2007-08-23 19:07:38 UTC (rev 79168)
+++ z3c.formjs/trunk/src/z3c/formjs/jsswitch.py	2007-08-23 19:08:29 UTC (rev 79169)
@@ -19,7 +19,8 @@
 import zope.component
 import zope.interface
 import zope.schema.interfaces
-from z3c.form.interfaces import IWidgets
+from z3c.form import button
+from z3c.form.interfaces import IWidgets, DISPLAY_MODE
 from zope.publisher.interfaces import NotFound
 
 from z3c.formjs import ajax, interfaces, jsevent, jsaction
@@ -55,6 +56,8 @@
 class WidgetModeSwitcher(ajax.AJAXRequestHandler):
     """A mix-in to forms to allow switching between widget modes."""
     zope.interface.implements(interfaces.IWidgetModeSwitcher)
+    buttons = button.Buttons()
+    mode = DISPLAY_MODE
 
     @jsaction.handler(zope.schema.interfaces.IField, jsevent.CLICK)
     def switchToInputWidget(self, event, selector):
@@ -88,7 +91,7 @@
         handlers = dict(widget.form.jshandlers.getHandlers(widget.field))
         code = handlers[jsevent.CLICK](
             jsevent.CLICK, jsaction.WidgetSelector(widget), self.request)
-        widget.onclick = unicode(code.replace('\n', ''))
+        widget.onclick = unicode(code.replace('\n', ' '))
         return widget.render()
 
     @ajax.handler
@@ -98,8 +101,7 @@
         handlers = dict(widget.form.jshandlers.getHandlers(widget.field))
         code = handlers[jsevent.BLUR](
             jsevent.BLUR, jsaction.WidgetSelector(widget), self.request)
-        widget.onblur = unicode(code.replace('\n', ''))
-        print widget.onblur
+        widget.onblur = unicode(code.replace('\n', ' '))
         return widget.render()
 
     @ajax.handler
@@ -111,3 +113,4 @@
             return errors[0].message
         self.applyChanges(data)
         return ''
+

Added: z3c.formjs/trunk/src/z3c/formjs/jsswitch.txt
===================================================================
--- z3c.formjs/trunk/src/z3c/formjs/jsswitch.txt	                        (rev 0)
+++ z3c.formjs/trunk/src/z3c/formjs/jsswitch.txt	2007-08-23 19:08:29 UTC (rev 79169)
@@ -0,0 +1,240 @@
+================================
+JavaScript Widget-Mode Switching
+================================
+
+This module implements widget-mode switching using the JavaScript features
+provided by this package.
+
+  >>> from z3c.formjs import jsswitch
+
+In other words, by default the form is shown in display mode. When the user
+clicks on the widget it switches the widget into input mode, allowing the user
+to change the widget's value. When the user moves the mouse cursor out of the
+widget and clicks somewhere else, the widget mode is switched back to display
+mode.
+
+Let's see how this works using a simple schema:
+
+  >>> import zope.interface
+  >>> import zope.schema
+
+  >>> class IPerson(zope.interface.Interface):
+  ...     name = zope.schema.TextLine(
+  ...         title=u'Name')
+  ...     age = zope.schema.Int(
+  ...         title=u'Age',
+  ...         min=0)
+
+Of course we need an implementation and an instance as well:
+
+  >>> class Person(object):
+  ...     zope.interface.implements(IPerson)
+  ...     def __init__(self, name, age):
+  ...         self.name = name
+  ...         self.age = age
+  ...     def __repr__(self):
+  ...         return '<%s %r (%r)>' % (
+  ...             self.__class__.__name__, self.name, self.age)
+
+  >>> stephan = Person(u'Stephan', 27)
+  >>> stephan
+  <Person u'Stephan' (27)>
+
+Let's now create an edit form for the person, that is also a widget mode
+switcher:
+
+  >>> from z3c.form import form, field, interfaces
+  >>> class ShowPerson(jsswitch.WidgetModeSwitcher, form.EditForm):
+  ...     fields = field.Fields(IPerson)
+
+You want the widget-mode switcher mix-in class to be left of the edit form
+base-class, because the widget mode switcher overrides some attributes, such
+as the form ``mode`` attribute.
+
+Let's now create an instance:
+
+  >>> from z3c.form.testing import TestRequest
+  >>> showPerson = ShowPerson(stephan, TestRequest())
+
+  >>> from z3c.formjs import testing
+  >>> testing.addTemplate(showPerson, 'simple_edit.pt')
+
+Before we can render the form, we need to register some necessary components:
+
+* Register all form default components:
+
+  >>> from z3c.form.testing import setupFormDefaults
+  >>> setupFormDefaults()
+
+* Register all necessary renderers, including the specific ones for this
+  module:
+
+  >>> from z3c.formjs import testing
+  >>> testing.setupRenderers()
+
+  >>> from z3c.formjs.jqueryrenderer import JQueryWidgetSwitcherRenderer
+  >>> zope.component.provideAdapter(JQueryWidgetSwitcherRenderer)
+
+  >>> from z3c.formjs.jqueryrenderer import JQueryWidgetSaverRenderer
+  >>> zope.component.provideAdapter(JQueryWidgetSaverRenderer)
+
+* Hook up the "provider" TALES expression type:
+
+  >>> from zope.app.pagetemplate.engine import TrustedEngine
+  >>> from zope.contentprovider import tales
+  >>> TrustedEngine.registerType('provider', tales.TALESProviderExpression)
+
+* Create a viewlet manager that does not require security to be setup:
+
+  >>> from zope.viewlet import manager
+  >>> class JSViewletManager(manager.ViewletManagerBase):
+  ...     def filter(self, viewlets):
+  ...         return viewlets
+
+* Register the viewlet manager as a content provider known as "javascript":
+
+  >>> from z3c.form.interfaces import IFormLayer
+  >>> from zope.contentprovider.interfaces import IContentProvider
+  >>> zope.component.provideAdapter(
+  ...     JSViewletManager,
+  ...     (None, IFormLayer, None),
+  ...     IContentProvider,
+  ...     name='javascript')
+
+* Register the JS Subscriber viewlet for this new viewlet manager:
+
+  >>> from zope.viewlet.interfaces import IViewlet
+  >>> from z3c.formjs import jsevent, interfaces
+  >>> zope.component.provideAdapter(
+  ...     jsevent.JSSubscriptionsViewlet,
+  ...     (None, IFormLayer, interfaces.IHaveJSSubscriptions,
+  ...      JSViewletManager), IViewlet, name='subscriptions')
+
+* Register the handler to subscriptions converter subscriber:
+
+  >>> from z3c.formjs import jsaction
+  >>> zope.component.provideHandler(jsaction.createSubscriptionsForWidget)
+
+Now we are ready to update and render the form:
+
+  >>> showPerson.update()
+  >>> print showPerson.render()
+  <html>
+    <head>
+      <script type="text/javascript">
+        $(document).ready(function(){
+          $("#form-widgets-name").bind("click",
+            function(){$.get('/switchWidget',
+              function(html){switchWidget("form-widgets-name", html)}
+          )});
+          $("#form-widgets-age").bind("click",
+            function(){$.get('/switchWidget',
+              function(html){switchWidget("form-widgets-age", html)}
+          )});
+          $("#form-widgets-name").bind("blur",
+            function(){$.get('saveValue',
+              function(msg){saveWidget("form-widgets-name", msg)}
+              );
+            $.get('/switchWidget',
+              function(html){switchWidget("form-widgets-name", html)}
+          )});
+          $("#form-widgets-age").bind("blur",
+            function(){$.get('saveValue',
+              function(msg){saveWidget("form-widgets-age", msg)}
+              );
+            $.get('/switchWidget',
+              function(html){switchWidget("form-widgets-age", html)}
+         )});
+      })
+    </script>
+    </head>
+    <body>
+      <form action=".">
+        <div class="row">
+          <label for="form-widgets-name">Name</label>
+          <span id="form-widgets-name"
+                class="text-widget required textline-field">
+            Stephan
+          </span>
+        </div>
+        <div class="row">
+          <label for="form-widgets-age">Age</label>
+          <span id="form-widgets-age"
+                class="text-widget required int-field">
+            27
+          </span>
+        </div>
+      </form>
+    </body>
+  </html>
+
+As you can see there are several subscribers on the widgets to allow for the
+switching. It is up to the user to implement the ``switchWidget()`` and
+``saveWidget()`` JavaScript functions that do all the work on the JavaScript
+end.
+
+When the user clicks on the age, for example, the ``getInputWidget()`` AJAX
+handler is called:
+
+  >>> showPerson.getInputWidget
+  <AJAXHandler 'getInputWidget'>
+
+  >>> showPerson.getInputWidget(showPerson)
+  Traceback (most recent call last):
+  ...
+  KeyError: 'widget-name'
+
+As you can see the call failed, because we forgot to specify the name of the
+widget for which we would like to get the input version for. Let's fix that
+problem:
+
+  >>> from z3c.form.testing import TestRequest
+  >>> showPerson = ShowPerson(stephan, TestRequest(form={'widget-name': 'age'}))
+  >>> showPerson.update()
+
+  >>> print showPerson.getInputWidget(showPerson)
+  <input type="text" id="form-widgets-age"
+         name="form.widgets.age"
+         class="text-widget required int-field"
+         onblur="$.get('saveValue',
+             function(msg){saveWidget(&quot;form-widgets-age&quot;, msg)} );
+           $.get('/switchWidget',
+             function(html){switchWidget(&quot;form-widgets-age&quot;, html)} )"
+         value="27" />
+
+Once the editing is complete, the value gets saved:
+
+  >>> showPerson = ShowPerson(stephan, TestRequest(form={
+  ...     'widget-name': 'age',
+  ...     'form.widgets.age': '25'
+  ...     }))
+  >>> showPerson.update()
+
+  >>> showPerson.saveWidgetValue(showPerson)
+  ''
+  >>> stephan.age
+  25
+
+When the value has errors, an error message is returned:
+
+  >>> showPerson = ShowPerson(stephan, TestRequest(form={
+  ...     'widget-name': 'age',
+  ...     'form.widgets.age': '-1'
+  ...     }))
+  >>> showPerson.update()
+
+  >>> showPerson.saveWidgetValue(showPerson)
+  u'Value is too small'
+
+Once the value is saved, the widget becomes a display widget again:
+
+  >>> showPerson = ShowPerson(stephan, TestRequest(form={'widget-name': 'age'}))
+  >>> showPerson.update()
+
+  >>> print showPerson.getDisplayWidget(showPerson)
+  <span id="form-widgets-age"
+        class="text-widget required int-field"
+        onclick="$.get('/switchWidget',
+          function(html){switchWidget(&quot;form-widgets-age&quot;, html)} )">
+    25
+  </span>


Property changes on: z3c.formjs/trunk/src/z3c/formjs/jsswitch.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: z3c.formjs/trunk/src/z3c/formjs/testing.py
===================================================================
--- z3c.formjs/trunk/src/z3c/formjs/testing.py	2007-08-23 19:07:38 UTC (rev 79168)
+++ z3c.formjs/trunk/src/z3c/formjs/testing.py	2007-08-23 19:08:29 UTC (rev 79169)
@@ -108,11 +108,48 @@
         return "$.get('/validate', function(data){ alert(data) })"
 
 
+class WidgetSwitcherRenderer(object):
+    zope.component.adapts(interfaces.IWidgetSwitcher, IBrowserRequest)
+    zope.interface.implements(interfaces.IRenderer)
+
+    def __init__(self, switcher, request):
+        self.context = switcher
+        self.request = request
+
+    def update(self):
+        pass
+
+    def render(self):
+        widget = self.context.widget
+        switcherCall = 'switchWidget("%s", html)' % (widget.id)
+        return "$.get('/switchWidget', function(html){%s}\n)" % switcherCall
+
+
+class WidgetSaverRenderer(object):
+    zope.component.adapts(
+        interfaces.IWidgetSaver, IBrowserRequest)
+    zope.interface.implements(interfaces.IRenderer)
+
+    def __init__(self, switcher, request):
+        self.context = switcher
+        self.request = request
+
+    def update(self):
+        pass
+
+    def render(self):
+        widget = self.context.widget
+        saveCall = 'saveWidget("%s", msg)' % widget.id
+        return "$.get('saveValue', function(msg){%s}\n)" % saveCall
+
+
 def setupRenderers():
     zope.component.provideAdapter(IdSelectorRenderer)
     zope.component.provideAdapter(SubscriptionRenderer)
     zope.component.provideAdapter(ManagerRenderer)
     zope.component.provideAdapter(MessageValidationScriptRenderer)
+    zope.component.provideAdapter(WidgetSwitcherRenderer)
+    zope.component.provideAdapter(WidgetSaverRenderer)
 
 
 def addTemplate(form, filename):

Modified: z3c.formjs/trunk/src/z3c/formjs/tests/test_doc.py
===================================================================
--- z3c.formjs/trunk/src/z3c/formjs/tests/test_doc.py	2007-08-23 19:07:38 UTC (rev 79168)
+++ z3c.formjs/trunk/src/z3c/formjs/tests/test_doc.py	2007-08-23 19:08:29 UTC (rev 79169)
@@ -21,6 +21,11 @@
 
 from z3c.form import testing
 
+def tearDown(test):
+    testing.tearDown(test)
+    from zope.app.pagetemplate import engine
+    engine.TrustedEngine = engine._TrustedEngine()
+
 def test_suite():
     return unittest.TestSuite((
         zope.testing.doctest.DocFileSuite(
@@ -40,7 +45,7 @@
                         zope.testing.doctest.ELLIPSIS),
         zope.testing.doctest.DocFileSuite(
             '../jsaction.txt',
-            setUp=testing.setUp, tearDown=testing.tearDown,
+            setUp=testing.setUp, tearDown=tearDown,
             optionflags=zope.testing.doctest.NORMALIZE_WHITESPACE |
                         zope.testing.doctest.ELLIPSIS),
         zope.testing.doctest.DocFileSuite(
@@ -58,4 +63,9 @@
             setUp=testing.setUp, tearDown=testing.tearDown,
             optionflags=zope.testing.doctest.NORMALIZE_WHITESPACE |
                         zope.testing.doctest.ELLIPSIS),
+        zope.testing.doctest.DocFileSuite(
+            '../jsswitch.txt',
+            setUp=testing.setUp, tearDown=tearDown,
+            optionflags=zope.testing.doctest.NORMALIZE_WHITESPACE |
+                        zope.testing.doctest.ELLIPSIS),
         ))



More information about the Checkins mailing list