CheckboxesWidget: use a template and support extra options (#23413)
This commit is contained in:
parent
a1d072bd0f
commit
93d25abdda
|
@ -2,6 +2,7 @@ import datetime
|
|||
import sys
|
||||
import shutil
|
||||
import copy
|
||||
import collections
|
||||
|
||||
from quixote import cleanup
|
||||
from quixote.http_request import parse_query
|
||||
|
@ -457,6 +458,37 @@ def test_checkboxes_widget():
|
|||
mock_form_submission(req, widget, {'test$elementpeach': ['yes'], 'test$elementpear': ['yes']})
|
||||
assert widget.parse() == ['pear', 'peach']
|
||||
|
||||
def test_checkboxes_widget_without_name():
|
||||
widget = CheckboxesWidget('test', options=[('apple', 'Apple')])
|
||||
mock_form_submission(req, widget, {'test$element0': ['yes']})
|
||||
assert widget.parse() == ['apple']
|
||||
|
||||
def test_checkboxes_widget_submission_with_options():
|
||||
test_dt = datetime.datetime.now()
|
||||
widget = CheckboxesWidget('test',
|
||||
options_with_attributes=[
|
||||
('apple', 'Apple', 'apple', {'datetime': test_dt}),
|
||||
('pear', 'Pear', 'pear', {'datetime': test_dt}),
|
||||
('peach', 'Peach', 'peach', {'datetime': test_dt})])
|
||||
mock_form_submission(req, widget, {'test$elementpeach': ['yes'], 'test$elementpear': ['yes']})
|
||||
assert widget.parse() == ['pear', 'peach']
|
||||
|
||||
def test_checkboxes_get_options():
|
||||
test_dt = datetime.datetime.now()
|
||||
any_info = 'any extra info'
|
||||
widget = CheckboxesWidget('test',
|
||||
options_with_attributes=[
|
||||
('apple', 'Apple', 'apple', {'datetime': test_dt, 'any_info': any_info}),
|
||||
('pear', 'Pear', 'pear', {'datetime': test_dt}),
|
||||
('peach', 'Peach', 'peach', {'datetime': test_dt})])
|
||||
assert isinstance(widget.get_options(), collections.Iterator)
|
||||
iter_options = list(widget.get_options())
|
||||
assert len(iter_options) == 3
|
||||
for option in iter_options:
|
||||
assert isinstance(option, dict)
|
||||
assert isinstance(option['attributes'], dict)
|
||||
assert option['attributes']['datetime'] == test_dt
|
||||
assert iter_options[0]['attributes']['any_info'] == any_info
|
||||
|
||||
def test_composite_widget():
|
||||
widget = CompositeWidget('compotest')
|
||||
|
|
|
@ -1017,47 +1017,50 @@ class RegexStringWidget(StringWidget):
|
|||
|
||||
|
||||
class CheckboxesWidget(CompositeWidget):
|
||||
readonly = False
|
||||
template_name = 'qommon/forms/widgets/checkboxes.html'
|
||||
default_input_value = 'yes'
|
||||
|
||||
def __init__(self, name, value=None, options=None, **kwargs):
|
||||
'''Initialize the list of checkboxes with either options or options_with_attributes
|
||||
if you need to pass extra data to the child checkboxes
|
||||
'''
|
||||
CompositeWidget.__init__(self, name, value, **kwargs)
|
||||
self.element_names = collections.OrderedDict()
|
||||
|
||||
if kwargs.has_key('title'):
|
||||
del kwargs['title']
|
||||
if kwargs.has_key('readonly'):
|
||||
del kwargs['readonly']
|
||||
self.readonly = True
|
||||
self.readonly = kwargs.pop('readonly', False)
|
||||
self.inline = kwargs.get('inline', True)
|
||||
self.max_choices = int(kwargs.get('max_choices', 0) or 0)
|
||||
|
||||
self.options_with_attributes = kwargs.pop('options_with_attributes', None)
|
||||
self.disabled_options = []
|
||||
if self.options_with_attributes:
|
||||
options = self.options_with_attributes
|
||||
|
||||
for i, option in enumerate(options):
|
||||
self.extra_attributes = []
|
||||
# options_with_attributes is alternative to kwargs' options
|
||||
options_with_attributes = kwargs.pop('options_with_attributes', None)
|
||||
if options_with_attributes:
|
||||
options = options_with_attributes
|
||||
for position, option in enumerate(options):
|
||||
if len(option) == 2:
|
||||
# option passed alone are 3-tuples
|
||||
key, title = option[:2]
|
||||
name = 'element%d' % i
|
||||
name = 'element%d' % position
|
||||
else:
|
||||
_, title, key = option[:3]
|
||||
name = 'element%s' % str(key)
|
||||
# option are at least 4-tuples
|
||||
_, title, key = option[:3]
|
||||
name = 'element%s' % key
|
||||
if options_with_attributes:
|
||||
# option with attributes are in 4th position
|
||||
self.extra_attributes.append(option[3])
|
||||
if option[3].get('disabled'):
|
||||
self.disabled_options.append(name)
|
||||
else:
|
||||
self.extra_attributes.append(None)
|
||||
|
||||
key = str(key)
|
||||
|
||||
element_kwargs = kwargs.copy()
|
||||
if self.options_with_attributes and option[-1].get('disabled'):
|
||||
element_kwargs['disabled'] = 'disabled'
|
||||
self.disabled_options.append(name)
|
||||
|
||||
# the option is checked if its value matches the list of values (variable value)
|
||||
# of the parent component
|
||||
if value and key in value:
|
||||
checked = True
|
||||
else:
|
||||
checked = False
|
||||
self.add(CheckboxWidget, name, title=title, value=checked, **element_kwargs)
|
||||
self.element_names[name] = key
|
||||
self.add(CheckboxWidget, name, title=title, value=checked)
|
||||
|
||||
def _parse(self, request):
|
||||
if self.readonly:
|
||||
|
@ -1088,28 +1091,23 @@ class CheckboxesWidget(CompositeWidget):
|
|||
def has_error(self, request=None):
|
||||
return Widget.has_error(self, request=request)
|
||||
|
||||
def render_content(self):
|
||||
r = TemplateIO(html=True)
|
||||
if self.inline:
|
||||
r += htmltext('<ul class="inline">')
|
||||
else:
|
||||
r += htmltext('<ul>')
|
||||
for widget in self.get_widgets():
|
||||
if widget.attrs and 'disabled' in widget.attrs:
|
||||
r += htmltext('<li class="disabled"><label>')
|
||||
else:
|
||||
r += htmltext('<li><label>')
|
||||
if self.readonly:
|
||||
widget.attrs['disabled'] = 'disabled'
|
||||
if widget.value:
|
||||
r += htmltext('<input type="hidden" name="%s" value="yes" >') % widget.name
|
||||
widget.name = widget.name + 'xx'
|
||||
r += widget.render_content()
|
||||
r += htmltext('<span>%s</span>') % widget.title
|
||||
r += htmltext('</label>')
|
||||
r += htmltext('</li>')
|
||||
r += htmltext('</ul>')
|
||||
return r.getvalue()
|
||||
def get_options(self):
|
||||
'''Yield a dict with the input's context and pass along data attributes
|
||||
'''
|
||||
for idx, widget in enumerate(self.get_widgets()):
|
||||
option = {
|
||||
'name': widget.name,
|
||||
# If a checkbox is unchecked when its form is submitted,
|
||||
# there is no value submitted to the server to represent its unchecked state
|
||||
# https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox
|
||||
'value': self.default_input_value,
|
||||
'title': widget.title,
|
||||
'checked': True if widget.value else False,
|
||||
'disabled': True if widget.name in self.disabled_options or self.readonly else False,
|
||||
'attributes': self.extra_attributes[idx] or None
|
||||
}
|
||||
yield option
|
||||
|
||||
|
||||
class ValidatedStringWidget(StringWidget):
|
||||
'''StringWidget which checks the value entered is correct according to a regex'''
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
{% extends "qommon/forms/widget.html" %}
|
||||
{% block widget-control %}
|
||||
<ul {% if widget.inline %}class="inline"{% endif %}>
|
||||
{% for option in widget.get_options %}
|
||||
<li {% if option.disabled %}class="disabled"{% endif %}>
|
||||
<label>
|
||||
{% if widget.readonly %}<input type="hidden" name="{{option.name}}" value="{{option.value}}" >{% endif %}
|
||||
<input type="checkbox" value="{{option.value}}" name="{{option.name}}{% if option.disabled %}xx{% endif %}"{% if option.disabled %} disabled{% endif %}{% if option.checked %} checked="checked"{% endif %} id="form_{{option.name}}">
|
||||
<span>{{ option.title }}</span></label>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
Loading…
Reference in New Issue