[Checkins] SVN: z3c.widget/trunk/src/z3c/widget/optdropdown/ Implementation of an input widget for optchoice. Let's see how it works;

Stephan Richter srichter at cosmos.phy.tufts.edu
Thu Dec 28 14:13:16 EST 2006


Log message for revision 71660:
  Implementation of an input widget for optchoice. Let's see how it works; 
  I'll probably tweak it a little bit more.
  
  

Changed:
  A   z3c.widget/trunk/src/z3c/widget/optdropdown/
  A   z3c.widget/trunk/src/z3c/widget/optdropdown/DEPENDENCIES.cfg
  A   z3c.widget/trunk/src/z3c/widget/optdropdown/README.txt
  A   z3c.widget/trunk/src/z3c/widget/optdropdown/SETUP.cfg
  A   z3c.widget/trunk/src/z3c/widget/optdropdown/__init__.py
  A   z3c.widget/trunk/src/z3c/widget/optdropdown/configure.zcml
  A   z3c.widget/trunk/src/z3c/widget/optdropdown/tests.py
  A   z3c.widget/trunk/src/z3c/widget/optdropdown/widget.py
  A   z3c.widget/trunk/src/z3c/widget/optdropdown/z3c.widget.optdropdown-configure.zcml

-=-
Added: z3c.widget/trunk/src/z3c/widget/optdropdown/DEPENDENCIES.cfg
===================================================================
--- z3c.widget/trunk/src/z3c/widget/optdropdown/DEPENDENCIES.cfg	2006-12-28 16:20:08 UTC (rev 71659)
+++ z3c.widget/trunk/src/z3c/widget/optdropdown/DEPENDENCIES.cfg	2006-12-28 19:13:13 UTC (rev 71660)
@@ -0,0 +1 @@
+z3c.field.optchoice

Added: z3c.widget/trunk/src/z3c/widget/optdropdown/README.txt
===================================================================
--- z3c.widget/trunk/src/z3c/widget/optdropdown/README.txt	2006-12-28 16:20:08 UTC (rev 71659)
+++ z3c.widget/trunk/src/z3c/widget/optdropdown/README.txt	2006-12-28 19:13:13 UTC (rev 71660)
@@ -0,0 +1,351 @@
+=========================
+Optional Dropdown Widgets
+=========================
+
+The Optional Dropdown Widget simulates the common desktop widget of a combo
+box, which can also receive a custom entry.
+
+Before we can start, we have to do a little bit of setup:
+
+  >>> import zope.component
+  >>> import zope.schema
+  >>> import zope.app.form.browser
+  >>> from zope.publisher.interfaces.browser import IBrowserRequest
+
+  >>> zope.component.provideAdapter(
+  ...     zope.app.form.browser.TextWidget,
+  ...     (zope.schema.interfaces.ITextLine, IBrowserRequest),
+  ...     zope.app.form.interfaces.IInputWidget)
+
+
+First we have to create a field and a request:
+
+  >>> from z3c.schema.optchoice import OptionalChoice
+
+  >>> optchoice = OptionalChoice(
+  ...     __name__='occupation',
+  ...     title=u'Occupation',
+  ...     description=u'The Occupation',
+  ...     values=(u'Programmer', u'Designer', u'Project Manager'),
+  ...     value_type=zope.schema.TextLine())
+
+  >>> from zope.publisher.browser import TestRequest
+  >>> request = TestRequest()
+
+Now we can initialize widget.
+
+  >>> class Content(object):
+  ...     occupation = None
+  >>> content = Content()
+  >>> boundOptChoice = optchoice.bind(content)
+
+  >>> from z3c.widget.optdropdown import OptionalDropdownWidget
+  >>> widget = OptionalDropdownWidget(boundOptChoice, request)
+
+Let's make sure that all fields have the correct value:
+
+  >>> widget.name
+  'field.occupation'
+
+  >>> widget.label
+  u'Occupation'
+
+  >>> widget.hint
+  u'The Occupation'
+
+  >>> widget.visible
+  True
+
+  >>> widget.required
+  True
+
+The constructor should have also created 2 widgets:
+
+  >>> widget.customWidget
+  <zope.app.form.browser.textwidgets.TextWidget object at ...>
+  >>> widget.dropdownWidget
+  <zope.app.form.browser.itemswidgets.DropdownWidget object at ...>
+
+
+``setRenderedValue(value)`` Method
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The first method is ``setRenderedValue()``. The widget has two use cases,
+based on the type of value. If the value is a custom value, it will
+send the information to the custom widget:
+
+  >>> print widget.customWidget()
+  <... value="" />
+  >>> 'selected=""' in widget.dropdownWidget()
+  False
+
+  >>> widget.setRenderedValue(u'Scientist')
+
+  >>> print widget.customWidget()
+  <... value="Scientist" />
+  >>> 'selected=""' in widget.dropdownWidget()
+  False
+
+After resetting the widget passing in one of the choices in the
+vocabulary, the value should be displayed in the dropdown:
+
+  >>> widget.setRenderedValue(u'Designer')
+
+  >>> print widget.customWidget()
+  <... value="" />
+  >>> print widget.dropdownWidget()
+  <div>
+  ...
+  <option selected="selected" value="Designer">Designer</option>
+  ...
+  </div>
+
+
+``setPrefix(prefix)`` Method
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The prefix determines the name of the widget and the sub-widgets.
+
+  >>> widget.name
+  'field.occupation'
+  >>> widget.dropdownWidget.name
+  'field.occupation.occupation'
+  >>> widget.customWidget.name
+  'field.occupation.custom'
+
+  >>> widget.setPrefix('test.')
+
+  >>> widget.name
+  'test.occupation'
+  >>> widget.dropdownWidget.name
+  'test.occupation.occupation'
+  >>> widget.customWidget.name
+  'test.occupation.custom'
+
+
+``getInputValue()`` Method
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This method returns a value based on the input; the data is assumed to
+be valid. In our case that means, if we entered a custom value, it is
+returned:
+
+  >>> request = TestRequest(form={
+  ...     'field.occupation.custom': u'Teacher'})
+
+  >>> widget = OptionalDropdownWidget(boundOptChoice, request)
+
+  >>> widget.getInputValue()
+  u'Teacher'
+
+On the other hand, if we selected a choice from the vocabulary, it should be
+returned:
+
+  >>> request = TestRequest(form={
+  ...     'field.occupation.occupation': u'Designer'})
+
+  >>> widget = OptionalDropdownWidget(boundOptChoice, request)
+
+  >>> widget.getInputValue()
+  u'Designer'
+
+
+``applyChanges(content)`` Method
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This method applies the new value to the passed content. However, it
+must be smart enough to detect whether the values really changed.
+
+  >>> request = TestRequest(form={
+  ...     'field.occupation.custom': u'Teacher'})
+
+  >>> widget = OptionalDropdownWidget(boundOptChoice, request)
+  >>> widget.applyChanges(content)
+  True
+  >>> content.occupation
+  u'Teacher'
+
+  >>> widget.applyChanges(content)
+  False
+
+  >>> request = TestRequest(form={
+  ...     'field.occupation.occupation': u'Designer'})
+
+  >>> widget = OptionalDropdownWidget(boundOptChoice, request)
+
+  >>> widget.applyChanges(content)
+  True
+  >>> content.occupation
+  u'Designer'
+
+  >>> widget.applyChanges(content)
+  False
+
+
+``hasInput()`` Method
+~~~~~~~~~~~~~~~~~~~~~
+
+This mehtod checks for any input, but does not validate it. In our case this
+means that either a choice has been selected or the the custom value has been
+entered.
+
+  >>> request = TestRequest()
+  >>> widget = OptionalDropdownWidget(boundOptChoice, request)
+  >>> widget.hasInput()
+  False
+
+  >>> request = TestRequest(form={
+  ...     'field.occupation.custom': u'Teacher\nBad Stuff'})
+  >>> widget = OptionalDropdownWidget(boundOptChoice, request)
+  >>> widget.hasInput()
+  True
+
+  >>> request = TestRequest(form={
+  ...     'field.occupation.occupation': u'Waitress'})
+  >>> widget = OptionalDropdownWidget(boundOptChoice, request)
+  >>> widget.hasInput()
+  True
+
+
+``hasValidInput()`` Method
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Additionally to checking for any input, this method also checks whether the
+input is valid:
+
+  >>> request = TestRequest()
+  >>> widget = OptionalDropdownWidget(boundOptChoice, request)
+  >>> widget.hasValidInput()
+  False
+
+  >>> request = TestRequest(form={
+  ...     'field.occupation.occupation': u'Waitress'})
+  >>> widget = OptionalDropdownWidget(boundOptChoice, request)
+  >>> widget.hasValidInput()
+  False
+
+  >>> request = TestRequest(form={
+  ...     'field.occupation.occupation': u'Designer'})
+  >>> widget = OptionalDropdownWidget(boundOptChoice, request)
+  >>> widget.hasValidInput()
+  True
+
+  >>> request = TestRequest(form={
+  ...     'field.occupation.custom': u'Teacher\nBad Stuff'})
+  >>> widget = OptionalDropdownWidget(boundOptChoice, request)
+  >>> widget.hasValidInput()
+  False
+
+  >>> request = TestRequest(form={
+  ...     'field.occupation.custom': u'Teacher'})
+  >>> widget = OptionalDropdownWidget(boundOptChoice, request)
+  >>> widget.hasValidInput()
+  True
+
+
+hidden() Method
+~~~~~~~~~~~~~~~
+
+This method is implemented by simply concatenating the two widget's hidden
+output:
+
+  >>> request = TestRequest()
+  >>> widget = OptionalDropdownWidget(boundOptChoice, request)
+  >>> widget.setRenderedValue(u'Designer')
+  >>> print widget.hidden()
+  <input class="hiddenType" id="field.occupation.occupation"
+         name="field.occupation.occupation" type="hidden" value="Designer"  />
+  <input class="hiddenType" id="field.occupation.custom"
+         name="field.occupation.custom" type="hidden" value=""  />
+
+  >>> widget.setRenderedValue(u'Teacher')
+  >>> print widget.hidden()
+  <input class="hiddenType" id="field.occupation.occupation"
+         name="field.occupation.occupation" type="hidden" value=""  />
+  <input class="hiddenType" id="field.occupation.custom"
+         name="field.occupation.custom" type="hidden" value="Teacher"  />
+
+
+error() Method
+~~~~~~~~~~~~~~
+
+Again, we have our two cases. If an error occured in the dropdown, it is
+reported:
+
+  >>> from zope.app.form.interfaces import IWidgetInputError
+  >>> from zope.app.form.browser.exception import WidgetInputErrorView
+  >>> from zope.app.form.browser.interfaces import IWidgetInputErrorView
+
+  >>> zope.component.provideAdapter(
+  ...     WidgetInputErrorView,
+  ...     (IWidgetInputError, IBrowserRequest), IWidgetInputErrorView)
+
+  >>> request = TestRequest(form={
+  ...     'field.occupation.occupation': u'Designer'})
+  >>> widget = OptionalDropdownWidget(boundOptChoice, request)
+  >>> widget.getInputValue()
+  u'Designer'
+  >>> widget.error()
+  ''
+
+  >>> request = TestRequest(form={
+  ...     'field.occupation.occupation': u'Waitress'})
+  >>> widget = OptionalDropdownWidget(boundOptChoice, request)
+
+  >>> widget.getInputValue()
+  Traceback (most recent call last):
+  ...
+  ConversionError: ('Invalid value', token u'Waitress' not found in vocabulary)
+  >>> widget.error()
+  u'<span class="error">Invalid value</span>'
+
+Otherwise the custom widget's errors are reported:
+
+  >>> request = TestRequest(form={
+  ...     'field.occupation.custom': u'Teacher'})
+  >>> widget = OptionalDropdownWidget(boundOptChoice, request)
+  >>> widget.getInputValue()
+  u'Teacher'
+  >>> widget.error()
+  ''
+
+  >>> request = TestRequest(form={
+  ...     'field.occupation.custom': u'Teacher\nBad Stuff'})
+  >>> widget = OptionalDropdownWidget(boundOptChoice, request)
+
+  >>> widget.getInputValue()
+  Traceback (most recent call last):
+  ...
+  WidgetInputError: ('custom', u'', Teacher Bad Stuff)
+  >>> widget.error()
+  u'<span class="error">Constraint not satisfied</span>'
+
+
+__call__() Method
+~~~~~~~~~~~~~~~~~
+
+This method renders the widget using the sub-widgets. It simply adds the two
+widgets' output placing the ``connector between them:
+
+  >>> request = TestRequest(form={
+  ...     'field.occupation.custom': u'Teacher'})
+
+  >>> widget = OptionalDropdownWidget(boundOptChoice, request)
+  >>> widget.connector
+  u'<br />\n'
+
+  >>> print widget()
+  <div>
+  <div class="value">
+  <select id="field.occupation.occupation"
+          name="field.occupation.occupation" size="1" >
+  <option value="Programmer">Programmer</option>
+  <option value="Designer">Designer</option>
+  <option value="Project Manager">Project Manager</option>
+  </select>
+  </div>
+  <input name="field.occupation.occupation-empty-marker" type="hidden"
+         value="1" />
+  </div><br />
+  <input class="textType" id="field.occupation.custom"
+         name="field.occupation.custom" size="20" type="text" value="Teacher" />


Property changes on: z3c.widget/trunk/src/z3c/widget/optdropdown/README.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.widget/trunk/src/z3c/widget/optdropdown/SETUP.cfg
===================================================================
--- z3c.widget/trunk/src/z3c/widget/optdropdown/SETUP.cfg	2006-12-28 16:20:08 UTC (rev 71659)
+++ z3c.widget/trunk/src/z3c/widget/optdropdown/SETUP.cfg	2006-12-28 19:13:13 UTC (rev 71660)
@@ -0,0 +1,3 @@
+<data-files zopeskel/etc/package-includes>
+  z3c.widget.optdropdown-*.zcml
+</data-files>

Added: z3c.widget/trunk/src/z3c/widget/optdropdown/__init__.py
===================================================================
--- z3c.widget/trunk/src/z3c/widget/optdropdown/__init__.py	2006-12-28 16:20:08 UTC (rev 71659)
+++ z3c.widget/trunk/src/z3c/widget/optdropdown/__init__.py	2006-12-28 19:13:13 UTC (rev 71660)
@@ -0,0 +1,19 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Optional Dropdown Widget
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+from z3c.widget.optdropdown.widget import OptionalDropdownWidget


Property changes on: z3c.widget/trunk/src/z3c/widget/optdropdown/__init__.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.widget/trunk/src/z3c/widget/optdropdown/configure.zcml
===================================================================
--- z3c.widget/trunk/src/z3c/widget/optdropdown/configure.zcml	2006-12-28 16:20:08 UTC (rev 71659)
+++ z3c.widget/trunk/src/z3c/widget/optdropdown/configure.zcml	2006-12-28 19:13:13 UTC (rev 71660)
@@ -0,0 +1,13 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    i18n_domain="z3c.widget">
+
+  <view
+      type="zope.publisher.interfaces.browser.IBrowserRequest"
+      for="z3c.schema.optchoice.IOptionalCoice"
+      provides="zope.app.form.interfaces.IInputWidget"
+      factory=".widget.IOptionalDropDown"
+      permission="zope.Public"
+      />
+
+</configure>


Property changes on: z3c.widget/trunk/src/z3c/widget/optdropdown/configure.zcml
___________________________________________________________________
Name: svn:eol-style
   + native

Added: z3c.widget/trunk/src/z3c/widget/optdropdown/tests.py
===================================================================
--- z3c.widget/trunk/src/z3c/widget/optdropdown/tests.py	2006-12-28 16:20:08 UTC (rev 71659)
+++ z3c.widget/trunk/src/z3c/widget/optdropdown/tests.py	2006-12-28 19:13:13 UTC (rev 71660)
@@ -0,0 +1,35 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Optional Dropdown Widget Tests
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import doctest
+import unittest
+from zope.testing import doctestunit
+from zope.app.testing import placelesssetup
+
+def test_suite():
+    return unittest.TestSuite((
+        doctestunit.DocFileSuite(
+            'README.txt',
+            setUp=placelesssetup.setUp, tearDown=placelesssetup.tearDown,
+            globs={'pprint': doctestunit.pprint},
+            optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS),
+        ))
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
+


Property changes on: z3c.widget/trunk/src/z3c/widget/optdropdown/tests.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.widget/trunk/src/z3c/widget/optdropdown/widget.py
===================================================================
--- z3c.widget/trunk/src/z3c/widget/optdropdown/widget.py	2006-12-28 16:20:08 UTC (rev 71659)
+++ z3c.widget/trunk/src/z3c/widget/optdropdown/widget.py	2006-12-28 19:13:13 UTC (rev 71660)
@@ -0,0 +1,118 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Optional Dropdown Widget
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import zope.component
+from zope.app import form
+from zope.app.form import browser
+
+class OptionalDropdownWidget(object):
+    """Optional Dropdown Widget"""
+    zope.interface.implements(browser.interfaces.IBrowserWidget,
+                              form.interfaces.IInputWidget)
+
+    connector = u'<br />\n'
+
+    _prefix = 'field.'
+
+    # See zope.app.form.interfaces.IWidget
+    name = None
+    label = property(lambda self: self.context.title)
+    hint = property(lambda self: self.context.description)
+    visible = True
+
+    # See zope.app.form.interfaces.IInputWidget
+    required = property(lambda self: self.context.required)
+
+    def __init__(self, field, request):
+        self.context = field
+        self.request = request
+        # Setup the custom value widget
+        field.value_type.__name__ = 'custom'
+        self.customWidget = zope.component.getMultiAdapter(
+            (field.value_type, request), form.interfaces.IInputWidget)
+        # Setup the dropdown widget
+        self.dropdownWidget = form.browser.DropdownWidget(
+            field, field.vocabulary, request)
+        # Setting the prefix again, sets everything up correctly
+        self.setPrefix(self._prefix)
+
+    def setRenderedValue(self, value):
+        """See zope.app.form.interfaces.IWidget"""
+        if value in self.context.vocabulary:
+            self.dropdownWidget.setRenderedValue(value)
+            self.customWidget.setRenderedValue(
+                self.context.value_type.missing_value)
+        else:
+            self.customWidget.setRenderedValue(value)
+            self.dropdownWidget.setRenderedValue(
+                self.context.missing_value)
+
+    def setPrefix(self, prefix):
+        """See zope.app.form.interfaces.IWidget"""
+        # Set the prefix locally
+        if not prefix.endswith("."):
+            prefix += '.'
+        self._prefix = prefix
+        self.name = prefix + self.context.__name__
+        self.customWidget.setPrefix(self.name+'.')
+        self.dropdownWidget.setPrefix(self.name+'.')
+
+    def getInputValue(self):
+        """See zope.app.form.interfaces.IInputWidget"""
+        if self.customWidget.hasInput():
+            return self.customWidget.getInputValue()
+        else:
+            return self.dropdownWidget.getInputValue()
+
+    def applyChanges(self, content):
+        """See zope.app.form.interfaces.IInputWidget"""
+        field = self.context
+        new_value = self.getInputValue()
+        old_value = field.query(content, self)
+        # The selection of an existing scoresystem has not changed
+        if new_value == old_value:
+            return False
+        field.set(content, new_value)
+        return True
+
+    def hasInput(self):
+        """See zope.app.form.interfaces.IInputWidget"""
+        return self.dropdownWidget.hasInput() or self.customWidget.hasInput()
+
+    def hasValidInput(self):
+        """See zope.app.form.interfaces.IInputWidget"""
+        return (self.dropdownWidget.hasValidInput() or
+                self.customWidget.hasValidInput())
+
+    def hidden(self):
+        """See zope.app.form.browser.interfaces.IBrowserWidget"""
+        return '\n'.join((self.dropdownWidget.hidden(),
+                          self.customWidget.hidden()))
+
+
+    def error(self):
+        """See zope.app.form.browser.interfaces.IBrowserWidget"""
+        dropdownError = self.dropdownWidget.error()
+        if dropdownError:
+            return dropdownError
+        return self.customWidget.error()
+
+
+    def __call__(self):
+        """See zope.app.form.browser.interfaces.IBrowserWidget"""
+        return self.connector.join((self.dropdownWidget(), self.customWidget()))


Property changes on: z3c.widget/trunk/src/z3c/widget/optdropdown/widget.py
___________________________________________________________________
Name: svn:keywords
   + Id

Added: z3c.widget/trunk/src/z3c/widget/optdropdown/z3c.widget.optdropdown-configure.zcml
===================================================================
--- z3c.widget/trunk/src/z3c/widget/optdropdown/z3c.widget.optdropdown-configure.zcml	2006-12-28 16:20:08 UTC (rev 71659)
+++ z3c.widget/trunk/src/z3c/widget/optdropdown/z3c.widget.optdropdown-configure.zcml	2006-12-28 19:13:13 UTC (rev 71660)
@@ -0,0 +1 @@
+<include package="z3c.widget.flashupload" />
\ No newline at end of file


Property changes on: z3c.widget/trunk/src/z3c/widget/optdropdown/z3c.widget.optdropdown-configure.zcml
___________________________________________________________________
Name: svn:eol-style
   + native



More information about the Checkins mailing list