• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

zopefoundation / z3c.form / 5104192483

pending completion
5104192483

push

github

web-flow
Config with pure python template (#114)

* Bumped version for breaking release.

* Drop support for Python 2.7, 3.5, 3.6.

* Add support for Python 3.11.

1316 of 1408 branches covered (93.47%)

Branch coverage included in aggregate %.

420 of 420 new or added lines in 39 files covered. (100.0%)

3716 of 3838 relevant lines covered (96.82%)

0.97 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.98
/src/z3c/form/browser/widget.py
1
##############################################################################
2
#
3
# Copyright (c) 2007 Zope Foundation and Contributors.
4
# All Rights Reserved.
5
#
6
# This software is subject to the provisions of the Zope Public License,
7
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
8
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11
# FOR A PARTICULAR PURPOSE.
12
#
13
##############################################################################
14
"""Widget Framework Implementation."""
1✔
15
__docformat__ = "reStructuredText"
1✔
16
import zope.interface
1✔
17
from zope.schema.fieldproperty import FieldProperty
1✔
18

19
from z3c.form.browser import interfaces
1✔
20
from z3c.form.interfaces import INPUT_MODE
1✔
21
from z3c.form.interfaces import IFieldWidget
1✔
22

23

24
class WidgetLayoutSupport:
1✔
25
    """Widget layout support"""
26

27
    def wrapCSSClass(self, klass, pattern='%(class)s'):
1✔
28
        """Return a list of css class names wrapped with given pattern"""
29
        if klass is not None and pattern is not None:
1!
30
            return [pattern % {'class': k} for k in klass.split()]
1✔
31
        else:
32
            return []
×
33

34
    def getCSSClass(self, klass=None, error=None, required=None,
1✔
35
                    classPattern='%(class)s', errorPattern='%(class)s-error',
36
                    requiredPattern='%(class)s-required'):
37
        """Setup given css class (klass) with error and required postfix
38

39
        If no klass name is given the widget.wrapper class name/names get used.
40
        It is also possible if more then one (empty space separated) names
41
        are given as klass argument.
42

43
        This method can get used from your form or widget template or widget
44
        layout template without to re-implement the widget itself just because
45
        you a different CSS class concept.
46

47
        The following sample:
48

49
        <div tal:attributes="class python:widget.getCSSClass('foo bar')">
50
          label widget and error
51
        </div>
52

53
        will render a div tag if the widget field defines required=True:
54

55
        <div class="foo-error bar-error foo-required bar-required foo bar">
56
          label widget and error
57
        </div>
58

59
        And the following sample:
60

61
        <div tal:attributes="class python:widget.getCSSClass('row')">
62
          label widget and error
63
        </div>
64

65
        will render a div tag if the widget field defines required=True
66
        and an error occurs:
67

68
        <div class="row-error row-required row">
69
          label widget and error
70
        </div>
71

72
        Note; you need to define a globale widget property if you use
73
        python:widget (in your form template). And you need to use the
74
        view scope in your widget or layout templates.
75

76
        Note, you can set the pattern to None for skip error or required
77
        rendering. Or you can use a pattern like 'error' or 'required' if
78
        you like to skip postfixing your default css klass name for error or
79
        required rendering.
80

81
        """
82
        classes = []
1✔
83
        # setup class names
84
        if klass is not None:
1!
85
            kls = klass
1✔
86
        else:
87
            kls = self.css
×
88

89
        # setup error class names
90
        if error is not None:
1!
91
            error = error
×
92
        else:
93
            error = kls
1✔
94

95
        # setup required class names
96
        if required is not None:
1!
97
            required = required
×
98
        else:
99
            required = kls
1✔
100

101
        # append error class names
102
        if self.error is not None:
1✔
103
            classes += self.wrapCSSClass(error, errorPattern)
1✔
104
        # append required class names
105
        if self.required:
1✔
106
            classes += self.wrapCSSClass(required, requiredPattern)
1✔
107
        # append given class names
108
        classes += self.wrapCSSClass(kls, classPattern)
1✔
109
        # remove duplicated class names but keep order
110
        unique = []
1✔
111
        [unique.append(kls) for kls in classes if kls not in unique]
1✔
112
        return ' '.join(unique)
1✔
113

114

115
@zope.interface.implementer(interfaces.IHTMLFormElement)
1✔
116
class HTMLFormElement(WidgetLayoutSupport):
1✔
117

118
    id = FieldProperty(interfaces.IHTMLFormElement['id'])
1✔
119
    klass = FieldProperty(interfaces.IHTMLFormElement['klass'])
1✔
120
    style = FieldProperty(interfaces.IHTMLFormElement['style'])
1✔
121
    title = FieldProperty(interfaces.IHTMLFormElement['title'])
1✔
122

123
    lang = FieldProperty(interfaces.IHTMLFormElement['lang'])
1✔
124

125
    onclick = FieldProperty(interfaces.IHTMLFormElement['onclick'])
1✔
126
    ondblclick = FieldProperty(interfaces.IHTMLFormElement['ondblclick'])
1✔
127
    onmousedown = FieldProperty(interfaces.IHTMLFormElement['onmousedown'])
1✔
128
    onmouseup = FieldProperty(interfaces.IHTMLFormElement['onmouseup'])
1✔
129
    onmouseover = FieldProperty(interfaces.IHTMLFormElement['onmouseover'])
1✔
130
    onmousemove = FieldProperty(interfaces.IHTMLFormElement['onmousemove'])
1✔
131
    onmouseout = FieldProperty(interfaces.IHTMLFormElement['onmouseout'])
1✔
132
    onkeypress = FieldProperty(interfaces.IHTMLFormElement['onkeypress'])
1✔
133
    onkeydown = FieldProperty(interfaces.IHTMLFormElement['onkeydown'])
1✔
134
    onkeyup = FieldProperty(interfaces.IHTMLFormElement['onkeyup'])
1✔
135

136
    disabled = FieldProperty(interfaces.IHTMLFormElement['disabled'])
1✔
137
    tabindex = FieldProperty(interfaces.IHTMLFormElement['tabindex'])
1✔
138
    onfocus = FieldProperty(interfaces.IHTMLFormElement['onfocus'])
1✔
139
    onblur = FieldProperty(interfaces.IHTMLFormElement['onblur'])
1✔
140
    onchange = FieldProperty(interfaces.IHTMLFormElement['onchange'])
1✔
141

142
    # layout support
143
    css = FieldProperty(interfaces.IHTMLFormElement['css'])
1✔
144

145
    def addClass(self, klass):
1✔
146
        """See interfaces.IHTMLFormElement"""
147
        if not self.klass:
1✔
148
            self.klass = str(klass)
1✔
149
        else:
150
            # make sure items are not repeated
151
            parts = self.klass.split() + [str(klass)]
1✔
152
            seen = {}
1✔
153
            unique = []
1✔
154
            for item in parts:
1✔
155
                if item in seen:
1✔
156
                    continue
1✔
157
                seen[item] = 1
1✔
158
                unique.append(item)
1✔
159
            self.klass = ' '.join(unique)
1✔
160

161
    def update(self):
1✔
162
        """See z3c.form.interfaces.IWidget"""
163
        super().update()
1✔
164
        if self.mode == INPUT_MODE and self.required:
1✔
165
            self.addClass('required')
1✔
166

167

168
@zope.interface.implementer(interfaces.IHTMLInputWidget)
1✔
169
class HTMLInputWidget(HTMLFormElement):
1✔
170

171
    readonly = FieldProperty(interfaces.IHTMLInputWidget['readonly'])
1✔
172
    alt = FieldProperty(interfaces.IHTMLInputWidget['alt'])
1✔
173
    accesskey = FieldProperty(interfaces.IHTMLInputWidget['accesskey'])
1✔
174
    onselect = FieldProperty(interfaces.IHTMLInputWidget['onselect'])
1✔
175

176

177
@zope.interface.implementer(interfaces.IHTMLTextInputWidget)
1✔
178
class HTMLTextInputWidget(HTMLInputWidget):
1✔
179

180
    size = FieldProperty(interfaces.IHTMLTextInputWidget['size'])
1✔
181
    maxlength = FieldProperty(interfaces.IHTMLTextInputWidget['maxlength'])
1✔
182
    placeholder = FieldProperty(interfaces.IHTMLTextInputWidget['placeholder'])
1✔
183
    autocapitalize = FieldProperty(
1✔
184
        interfaces.IHTMLTextInputWidget['autocapitalize'])
185

186

187
@zope.interface.implementer(interfaces.IHTMLTextAreaWidget)
1✔
188
class HTMLTextAreaWidget(HTMLFormElement):
1✔
189

190
    rows = FieldProperty(interfaces.IHTMLTextAreaWidget['rows'])
1✔
191
    cols = FieldProperty(interfaces.IHTMLTextAreaWidget['cols'])
1✔
192
    readonly = FieldProperty(interfaces.IHTMLTextAreaWidget['readonly'])
1✔
193
    accesskey = FieldProperty(interfaces.IHTMLTextAreaWidget['accesskey'])
1✔
194
    onselect = FieldProperty(interfaces.IHTMLTextAreaWidget['onselect'])
1✔
195

196

197
@zope.interface.implementer(interfaces.IHTMLSelectWidget)
1✔
198
class HTMLSelectWidget(HTMLFormElement):
1✔
199

200
    multiple = FieldProperty(interfaces.IHTMLSelectWidget['multiple'])
1✔
201
    size = FieldProperty(interfaces.IHTMLSelectWidget['size'])
1✔
202

203

204
def addFieldClass(widget):
1✔
205
    """Add a class to the widget that is based on the field type name.
206

207
    If the widget does not have field, then nothing is done.
208
    """
209
    if IFieldWidget.providedBy(widget):
1✔
210
        klass = str(widget.field.__class__.__name__.lower() + '-field')
1✔
211
        widget.addClass(klass)
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc