CheckboxesWidget: use a template and support extra options (#23413)

This commit is contained in:
Elias Showk 2018-05-04 18:58:01 +02:00
parent a1d072bd0f
commit 93d25abdda
3 changed files with 88 additions and 45 deletions

View File

@ -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')

View File

@ -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'''

View File

@ -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 %}