Refs #32338 -- Made RadioSelect/CheckboxSelectMultiple render in <div> tags.

This improves accessibility for screen reader users.
This commit is contained in:
David Smith 2021-06-11 07:39:12 +01:00 committed by Mariusz Felisiak
parent a5cb1ef6eb
commit 5942ab5eb1
9 changed files with 258 additions and 264 deletions

View File

@ -1,5 +1,5 @@
{% set id = widget.attrs.id %}<ul{% if id %} id="{{ id }}"{% endif %}{% if widget.attrs.class %} class="{{ widget.attrs.class }}"{% endif %}>{% for group, options, index in widget.optgroups %}{% if group %} {% set id = widget.attrs.id %}<div{% if id %} id="{{ id }}"{% endif %}{% if widget.attrs.class %} class="{{ widget.attrs.class }}"{% endif %}>{% for group, options, index in widget.optgroups %}{% if group %}
<li>{{ group }}<ul{% if id %} id="{{ id }}_{{ index }}"{% endif %}>{% endif %}{% for widget in options %} <div><label>{{ group }}</label>{% endif %}{% for widget in options %}<div>
<li>{% include widget.template_name %}</li>{% endfor %}{% if group %} {% include widget.template_name %}</div>{% endfor %}{% if group %}
</ul></li>{% endif %}{% endfor %} </div>{% endif %}{% endfor %}
</ul> </div>

View File

@ -1,5 +1,5 @@
{% with id=widget.attrs.id %}<ul{% if id %} id="{{ id }}"{% endif %}{% if widget.attrs.class %} class="{{ widget.attrs.class }}"{% endif %}>{% for group, options, index in widget.optgroups %}{% if group %} {% with id=widget.attrs.id %}<div{% if id %} id="{{ id }}"{% endif %}{% if widget.attrs.class %} class="{{ widget.attrs.class }}"{% endif %}>{% for group, options, index in widget.optgroups %}{% if group %}
<li>{{ group }}<ul{% if id %} id="{{ id }}_{{ index }}"{% endif %}>{% endif %}{% for option in options %} <div><label>{{ group }}</label>{% endif %}{% for option in options %}<div>
<li>{% include option.template_name with widget=option %}</li>{% endfor %}{% if group %} {% include option.template_name with widget=option %}</div>{% endfor %}{% if group %}
</ul></li>{% endif %}{% endfor %} </div>{% endif %}{% endfor %}
</ul>{% endwith %} </div>{% endwith %}

View File

@ -703,14 +703,19 @@ that specifies the template used to render each choice. For example, for the
* ``option_template_name``: ``'django/forms/widgets/radio_option.html'`` * ``option_template_name``: ``'django/forms/widgets/radio_option.html'``
Similar to :class:`Select`, but rendered as a list of radio buttons within Similar to :class:`Select`, but rendered as a list of radio buttons within
``<li>`` tags: ``<div>`` tags:
.. code-block:: html .. code-block:: html
<ul> <div>
<li><input type="radio" name="..."></li> <div><input type="radio" name="..."></div>
... ...
</ul> </div>
.. versionchanged:: 4.0
So they are announced more concisely by screen readers, radio buttons
were changed to render in ``<div>`` tags.
For more granular control over the generated markup, you can loop over the For more granular control over the generated markup, you can loop over the
radio buttons in the template. Assuming a form ``myform`` with a field radio buttons in the template. Assuming a form ``myform`` with a field
@ -788,10 +793,10 @@ that specifies the template used to render each choice. For example, for the
</fieldset> </fieldset>
If you decide not to loop over the radio buttons -- e.g., if your template If you decide not to loop over the radio buttons -- e.g., if your template
includes ``{{ myform.beatles }}`` -- they'll be output in a ``<ul>`` with includes ``{{ myform.beatles }}`` -- they'll be output in a ``<div>`` with
``<li>`` tags, as above. ``<div>`` tags, as above.
The outer ``<ul>`` container receives the ``id`` attribute of the widget, The outer ``<div>`` container receives the ``id`` attribute of the widget,
if defined, or :attr:`BoundField.auto_id` otherwise. if defined, or :attr:`BoundField.auto_id` otherwise.
When looping over the radio buttons, the ``label`` and ``input`` tags include When looping over the radio buttons, the ``label`` and ``input`` tags include
@ -810,14 +815,19 @@ that specifies the template used to render each choice. For example, for the
.. code-block:: html .. code-block:: html
<ul> <div>
<li><input type="checkbox" name="..." ></li> <div><input type="checkbox" name="..." ></div>
... ...
</ul> </div>
The outer ``<ul>`` container receives the ``id`` attribute of the widget, The outer ``<div>`` container receives the ``id`` attribute of the widget,
if defined, or :attr:`BoundField.auto_id` otherwise. if defined, or :attr:`BoundField.auto_id` otherwise.
.. versionchanged:: 4.0
So they are announced more concisely by screen readers, checkboxes were
changed to render in ``<div>`` tags.
Like :class:`RadioSelect`, you can loop over the individual checkboxes for the Like :class:`RadioSelect`, you can loop over the individual checkboxes for the
widget's choices. Unlike :class:`RadioSelect`, the checkboxes won't include the widget's choices. Unlike :class:`RadioSelect`, the checkboxes won't include the
``required`` HTML attribute if the field is required because browser validation ``required`` HTML attribute if the field is required because browser validation

View File

@ -551,6 +551,13 @@ Miscellaneous
``django.db.migrations.state.ProjectState.__init__()`` method must now be a ``django.db.migrations.state.ProjectState.__init__()`` method must now be a
set if provided. set if provided.
* :class:`~django.forms.RadioSelect` and
:class:`~django.forms.CheckboxSelectMultiple` widgets are now rendered in
``<div>`` tags so they are announced more concisely by screen readers. If you
need the previous behavior, :ref:`override the widget template
<overriding-built-in-widget-templates>` with the appropriate template from
Django 3.2.
.. _deprecated-features-4.0: .. _deprecated-features-4.0:
Features deprecated in 4.0 Features deprecated in 4.0

View File

@ -585,20 +585,20 @@ class FormsTestCase(SimpleTestCase):
language = ChoiceField(choices=[('P', 'Python'), ('J', 'Java')], widget=RadioSelect) language = ChoiceField(choices=[('P', 'Python'), ('J', 'Java')], widget=RadioSelect)
f = FrameworkForm(auto_id=False) f = FrameworkForm(auto_id=False)
self.assertHTMLEqual(str(f['language']), """<ul> self.assertHTMLEqual(str(f['language']), """<div>
<li><label><input type="radio" name="language" value="P" required> Python</label></li> <div><label><input type="radio" name="language" value="P" required> Python</label></div>
<li><label><input type="radio" name="language" value="J" required> Java</label></li> <div><label><input type="radio" name="language" value="J" required> Java</label></div>
</ul>""") </div>""")
self.assertHTMLEqual(f.as_table(), """<tr><th>Name:</th><td><input type="text" name="name" required></td></tr> self.assertHTMLEqual(f.as_table(), """<tr><th>Name:</th><td><input type="text" name="name" required></td></tr>
<tr><th>Language:</th><td><ul> <tr><th>Language:</th><td><div>
<li><label><input type="radio" name="language" value="P" required> Python</label></li> <div><label><input type="radio" name="language" value="P" required> Python</label></div>
<li><label><input type="radio" name="language" value="J" required> Java</label></li> <div><label><input type="radio" name="language" value="J" required> Java</label></div>
</ul></td></tr>""") </div></td></tr>""")
self.assertHTMLEqual(f.as_ul(), """<li>Name: <input type="text" name="name" required></li> self.assertHTMLEqual(f.as_ul(), """<li>Name: <input type="text" name="name" required></li>
<li>Language: <ul> <li>Language: <div>
<li><label><input type="radio" name="language" value="P" required> Python</label></li> <div><label><input type="radio" name="language" value="P" required> Python</label></div>
<li><label><input type="radio" name="language" value="J" required> Java</label></li> <div><label><input type="radio" name="language" value="J" required> Java</label></div>
</ul></li>""") </div></li>""")
# Regarding auto_id and <label>, RadioSelect is a special case. Each radio button # Regarding auto_id and <label>, RadioSelect is a special case. Each radio button
# gets a distinct ID, formed by appending an underscore plus the button's # gets a distinct ID, formed by appending an underscore plus the button's
@ -606,12 +606,12 @@ class FormsTestCase(SimpleTestCase):
f = FrameworkForm(auto_id='id_%s') f = FrameworkForm(auto_id='id_%s')
self.assertHTMLEqual( self.assertHTMLEqual(
str(f['language']), str(f['language']),
"""<ul id="id_language"> """<div id="id_language">
<li><label for="id_language_0"><input type="radio" id="id_language_0" value="P" name="language" required> <div><label for="id_language_0"><input type="radio" id="id_language_0" value="P" name="language" required>
Python</label></li> Python</label></div>
<li><label for="id_language_1"><input type="radio" id="id_language_1" value="J" name="language" required> <div><label for="id_language_1"><input type="radio" id="id_language_1" value="J" name="language" required>
Java</label></li> Java</label></div>
</ul>""" </div>"""
) )
# When RadioSelect is used with auto_id, and the whole form is printed # When RadioSelect is used with auto_id, and the whole form is printed
@ -621,32 +621,32 @@ Java</label></li>
self.assertHTMLEqual( self.assertHTMLEqual(
f.as_table(), f.as_table(),
"""<tr><th><label for="id_name">Name:</label></th><td><input type="text" name="name" id="id_name" required></td></tr> """<tr><th><label for="id_name">Name:</label></th><td><input type="text" name="name" id="id_name" required></td></tr>
<tr><th><label>Language:</label></th><td><ul id="id_language"> <tr><th><label>Language:</label></th><td><div id="id_language">
<li><label for="id_language_0"><input type="radio" id="id_language_0" value="P" name="language" required> <div><label for="id_language_0"><input type="radio" id="id_language_0" value="P" name="language" required>
Python</label></li> Python</label></div>
<li><label for="id_language_1"><input type="radio" id="id_language_1" value="J" name="language" required> <div><label for="id_language_1"><input type="radio" id="id_language_1" value="J" name="language" required>
Java</label></li> Java</label></div>
</ul></td></tr>""" </div></td></tr>"""
) )
self.assertHTMLEqual( self.assertHTMLEqual(
f.as_ul(), f.as_ul(),
"""<li><label for="id_name">Name:</label> <input type="text" name="name" id="id_name" required></li> """<li><label for="id_name">Name:</label> <input type="text" name="name" id="id_name" required></li>
<li><label>Language:</label> <ul id="id_language"> <li><label>Language:</label> <div id="id_language">
<li><label for="id_language_0"><input type="radio" id="id_language_0" value="P" name="language" required> <div><label for="id_language_0"><input type="radio" id="id_language_0" value="P" name="language" required>
Python</label></li> Python</label></div>
<li><label for="id_language_1"><input type="radio" id="id_language_1" value="J" name="language" required> <div><label for="id_language_1"><input type="radio" id="id_language_1" value="J" name="language" required>
Java</label></li> Java</label></div>
</ul></li>""" </div></li>"""
) )
self.assertHTMLEqual( self.assertHTMLEqual(
f.as_p(), f.as_p(),
"""<p><label for="id_name">Name:</label> <input type="text" name="name" id="id_name" required></p> """<p><label for="id_name">Name:</label> <input type="text" name="name" id="id_name" required></p>
<p><label>Language:</label> <ul id="id_language"> <p><label>Language:</label> <div id="id_language">
<li><label for="id_language_0"><input type="radio" id="id_language_0" value="P" name="language" required> <div><label for="id_language_0"><input type="radio" id="id_language_0" value="P" name="language" required>
Python</label></li> Python</label></div>
<li><label for="id_language_1"><input type="radio" id="id_language_1" value="J" name="language" required> <div><label for="id_language_1"><input type="radio" id="id_language_1" value="J" name="language" required>
Java</label></li> Java</label></div>
</ul></p>""" </div></p>"""
) )
# Test iterating on individual radios in a template # Test iterating on individual radios in a template
@ -870,20 +870,20 @@ Java</label></li>
) )
f = SongForm(auto_id=False) f = SongForm(auto_id=False)
self.assertHTMLEqual(str(f['composers']), """<ul> self.assertHTMLEqual(str(f['composers']), """<div>
<li><label><input type="checkbox" name="composers" value="J"> John Lennon</label></li> <div><label><input type="checkbox" name="composers" value="J"> John Lennon</label></div>
<li><label><input type="checkbox" name="composers" value="P"> Paul McCartney</label></li> <div><label><input type="checkbox" name="composers" value="P"> Paul McCartney</label></div>
</ul>""") </div>""")
f = SongForm({'composers': ['J']}, auto_id=False) f = SongForm({'composers': ['J']}, auto_id=False)
self.assertHTMLEqual(str(f['composers']), """<ul> self.assertHTMLEqual(str(f['composers']), """<div>
<li><label><input checked type="checkbox" name="composers" value="J"> John Lennon</label></li> <div><label><input checked type="checkbox" name="composers" value="J"> John Lennon</label></div>
<li><label><input type="checkbox" name="composers" value="P"> Paul McCartney</label></li> <div><label><input type="checkbox" name="composers" value="P"> Paul McCartney</label></div>
</ul>""") </div>""")
f = SongForm({'composers': ['J', 'P']}, auto_id=False) f = SongForm({'composers': ['J', 'P']}, auto_id=False)
self.assertHTMLEqual(str(f['composers']), """<ul> self.assertHTMLEqual(str(f['composers']), """<div>
<li><label><input checked type="checkbox" name="composers" value="J"> John Lennon</label></li> <div><label><input checked type="checkbox" name="composers" value="J"> John Lennon</label></div>
<li><label><input checked type="checkbox" name="composers" value="P"> Paul McCartney</label></li> <div><label><input checked type="checkbox" name="composers" value="P"> Paul McCartney</label></div>
</ul>""") </div>""")
# Test iterating on individual checkboxes in a template # Test iterating on individual checkboxes in a template
t = Template('{% for checkbox in form.composers %}<div class="mycheckbox">{{ checkbox }}</div>{% endfor %}') t = Template('{% for checkbox in form.composers %}<div class="mycheckbox">{{ checkbox }}</div>{% endfor %}')
self.assertHTMLEqual(t.render(Context({'form': f})), """<div class="mycheckbox"><label> self.assertHTMLEqual(t.render(Context({'form': f})), """<div class="mycheckbox"><label>
@ -905,12 +905,12 @@ Java</label></li>
f = SongForm(auto_id='%s_id') f = SongForm(auto_id='%s_id')
self.assertHTMLEqual( self.assertHTMLEqual(
str(f['composers']), str(f['composers']),
"""<ul id="composers_id"> """<div id="composers_id">
<li><label for="composers_id_0"> <div><label for="composers_id_0">
<input type="checkbox" name="composers" value="J" id="composers_id_0"> John Lennon</label></li> <input type="checkbox" name="composers" value="J" id="composers_id_0"> John Lennon</label></div>
<li><label for="composers_id_1"> <div><label for="composers_id_1">
<input type="checkbox" name="composers" value="P" id="composers_id_1"> Paul McCartney</label></li> <input type="checkbox" name="composers" value="P" id="composers_id_1"> Paul McCartney</label></div>
</ul>""" </div>"""
) )
def test_multiple_choice_list_data(self): def test_multiple_choice_list_data(self):

View File

@ -57,15 +57,15 @@ class FormsI18nTests(SimpleTestCase):
self.assertHTMLEqual( self.assertHTMLEqual(
f.as_p(), f.as_p(),
'<p><label>\xc5\xf8\xdf:</label>' '<p><label>\xc5\xf8\xdf:</label>'
'<ul id="id_somechoice">\n' '<div id="id_somechoice">\n'
'<li><label for="id_somechoice_0">' '<div><label for="id_somechoice_0">'
'<input type="radio" id="id_somechoice_0" value="\xc5" name="somechoice" required> ' '<input type="radio" id="id_somechoice_0" value="\xc5" name="somechoice" required> '
'En tied\xe4</label></li>\n' 'En tied\xe4</label></div>\n'
'<li><label for="id_somechoice_1">' '<div><label for="id_somechoice_1">'
'<input type="radio" id="id_somechoice_1" value="\xf8" name="somechoice" required> ' '<input type="radio" id="id_somechoice_1" value="\xf8" name="somechoice" required> '
'Mies</label></li>\n<li><label for="id_somechoice_2">' 'Mies</label></div>\n<div><label for="id_somechoice_2">'
'<input type="radio" id="id_somechoice_2" value="\xdf" name="somechoice" required> ' '<input type="radio" id="id_somechoice_2" value="\xdf" name="somechoice" required> '
'Nainen</label></li>\n</ul></p>' 'Nainen</label></div>\n</div></p>'
) )
# Translated error messages # Translated error messages
@ -77,14 +77,14 @@ class FormsI18nTests(SimpleTestCase):
'\u041e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c' '\u041e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c'
'\u043d\u043e\u0435 \u043f\u043e\u043b\u0435.</li></ul>\n' '\u043d\u043e\u0435 \u043f\u043e\u043b\u0435.</li></ul>\n'
'<p><label>\xc5\xf8\xdf:</label>' '<p><label>\xc5\xf8\xdf:</label>'
' <ul id="id_somechoice">\n<li><label for="id_somechoice_0">' ' <div id="id_somechoice">\n<div><label for="id_somechoice_0">'
'<input type="radio" id="id_somechoice_0" value="\xc5" name="somechoice" required> ' '<input type="radio" id="id_somechoice_0" value="\xc5" name="somechoice" required> '
'En tied\xe4</label></li>\n' 'En tied\xe4</label></div>\n'
'<li><label for="id_somechoice_1">' '<div><label for="id_somechoice_1">'
'<input type="radio" id="id_somechoice_1" value="\xf8" name="somechoice" required> ' '<input type="radio" id="id_somechoice_1" value="\xf8" name="somechoice" required> '
'Mies</label></li>\n<li><label for="id_somechoice_2">' 'Mies</label></div>\n<div><label for="id_somechoice_2">'
'<input type="radio" id="id_somechoice_2" value="\xdf" name="somechoice" required> ' '<input type="radio" id="id_somechoice_2" value="\xdf" name="somechoice" required> '
'Nainen</label></li>\n</ul></p>' 'Nainen</label></div>\n</div></p>'
) )
def test_select_translated_text(self): def test_select_translated_text(self):

View File

@ -11,39 +11,39 @@ class CheckboxSelectMultipleTest(WidgetTest):
widget = CheckboxSelectMultiple widget = CheckboxSelectMultiple
def test_render_value(self): def test_render_value(self):
self.check_html(self.widget(choices=self.beatles), 'beatles', ['J'], html=( self.check_html(self.widget(choices=self.beatles), 'beatles', ['J'], html="""
"""<ul> <div>
<li><label><input checked type="checkbox" name="beatles" value="J"> John</label></li> <div><label><input checked type="checkbox" name="beatles" value="J"> John</label></div>
<li><label><input type="checkbox" name="beatles" value="P"> Paul</label></li> <div><label><input type="checkbox" name="beatles" value="P"> Paul</label></div>
<li><label><input type="checkbox" name="beatles" value="G"> George</label></li> <div><label><input type="checkbox" name="beatles" value="G"> George</label></div>
<li><label><input type="checkbox" name="beatles" value="R"> Ringo</label></li> <div><label><input type="checkbox" name="beatles" value="R"> Ringo</label></div>
</ul>""" </div>
)) """)
def test_render_value_multiple(self): def test_render_value_multiple(self):
self.check_html(self.widget(choices=self.beatles), 'beatles', ['J', 'P'], html=( self.check_html(self.widget(choices=self.beatles), 'beatles', ['J', 'P'], html="""
"""<ul> <div>
<li><label><input checked type="checkbox" name="beatles" value="J"> John</label></li> <div><label><input checked type="checkbox" name="beatles" value="J"> John</label></div>
<li><label><input checked type="checkbox" name="beatles" value="P"> Paul</label></li> <div><label><input checked type="checkbox" name="beatles" value="P"> Paul</label></div>
<li><label><input type="checkbox" name="beatles" value="G"> George</label></li> <div><label><input type="checkbox" name="beatles" value="G"> George</label></div>
<li><label><input type="checkbox" name="beatles" value="R"> Ringo</label></li> <div><label><input type="checkbox" name="beatles" value="R"> Ringo</label></div>
</ul>""" </div>
)) """)
def test_render_none(self): def test_render_none(self):
""" """
If the value is None, none of the options are selected, even if the If the value is None, none of the options are selected, even if the
choices have an empty option. choices have an empty option.
""" """
self.check_html(self.widget(choices=(('', 'Unknown'),) + self.beatles), 'beatles', None, html=( self.check_html(self.widget(choices=(('', 'Unknown'),) + self.beatles), 'beatles', None, html="""
"""<ul> <div>
<li><label><input type="checkbox" name="beatles" value=""> Unknown</label></li> <div><label><input type="checkbox" name="beatles" value=""> Unknown</label></div>
<li><label><input type="checkbox" name="beatles" value="J"> John</label></li> <div><label><input type="checkbox" name="beatles" value="J"> John</label></div>
<li><label><input type="checkbox" name="beatles" value="P"> Paul</label></li> <div><label><input type="checkbox" name="beatles" value="P"> Paul</label></div>
<li><label><input type="checkbox" name="beatles" value="G"> George</label></li> <div><label><input type="checkbox" name="beatles" value="G"> George</label></div>
<li><label><input type="checkbox" name="beatles" value="R"> Ringo</label></li> <div><label><input type="checkbox" name="beatles" value="R"> Ringo</label></div>
</ul>""" </div>
)) """)
def test_nested_choices(self): def test_nested_choices(self):
nested_choices = ( nested_choices = (
@ -52,31 +52,23 @@ class CheckboxSelectMultipleTest(WidgetTest):
('Video', (('vhs', 'VHS'), ('dvd', 'DVD'))), ('Video', (('vhs', 'VHS'), ('dvd', 'DVD'))),
) )
html = """ html = """
<ul id="media"> <div id="media">
<li> <div> <label for="media_0">
<label for="media_0"><input id="media_0" name="nestchoice" type="checkbox" value="unknown"> Unknown</label> <input type="checkbox" name="nestchoice" value="unknown" id="media_0"> Unknown</label></div>
</li> <div>
<li>Audio<ul id="media_1"> <label>Audio</label>
<li> <div> <label for="media_1_0">
<label for="media_1_0"> <input checked type="checkbox" name="nestchoice" value="vinyl" id="media_1_0"> Vinyl</label></div>
<input checked id="media_1_0" name="nestchoice" type="checkbox" value="vinyl"> Vinyl <div> <label for="media_1_1">
</label> <input type="checkbox" name="nestchoice" value="cd" id="media_1_1"> CD</label></div>
</li> </div><div>
<li> <label>Video</label>
<label for="media_1_1"><input id="media_1_1" name="nestchoice" type="checkbox" value="cd"> CD</label> <div> <label for="media_2_0">
</li> <input type="checkbox" name="nestchoice" value="vhs" id="media_2_0"> VHS</label></div>
</ul></li> <div> <label for="media_2_1">
<li>Video<ul id="media_2"> <input type="checkbox" name="nestchoice" value="dvd" id="media_2_1" checked> DVD</label></div>
<li> </div>
<label for="media_2_0"><input id="media_2_0" name="nestchoice" type="checkbox" value="vhs"> VHS</label> </div>
</li>
<li>
<label for="media_2_1">
<input checked id="media_2_1" name="nestchoice" type="checkbox" value="dvd"> DVD
</label>
</li>
</ul></li>
</ul>
""" """
self.check_html( self.check_html(
self.widget(choices=nested_choices), 'nestchoice', ('vinyl', 'dvd'), self.widget(choices=nested_choices), 'nestchoice', ('vinyl', 'dvd'),
@ -90,31 +82,18 @@ class CheckboxSelectMultipleTest(WidgetTest):
('Video', (('vhs', 'VHS'), ('dvd', 'DVD'))), ('Video', (('vhs', 'VHS'), ('dvd', 'DVD'))),
) )
html = """ html = """
<ul> <div>
<li> <div> <label><input type="checkbox" name="nestchoice" value="unknown"> Unknown</label></div>
<label><input name="nestchoice" type="checkbox" value="unknown"> Unknown</label> <div>
</li> <label>Audio</label>
<li>Audio<ul> <div> <label><input checked type="checkbox" name="nestchoice" value="vinyl"> Vinyl</label></div>
<li> <div> <label><input type="checkbox" name="nestchoice" value="cd"> CD</label></div>
<label> </div><div>
<input checked name="nestchoice" type="checkbox" value="vinyl"> Vinyl <label>Video</label>
</label> <div> <label><input type="checkbox" name="nestchoice" value="vhs"> VHS</label></div>
</li> <div> <label><input type="checkbox" name="nestchoice" value="dvd"checked> DVD</label></div>
<li> </div>
<label><input name="nestchoice" type="checkbox" value="cd"> CD</label> </div>
</li>
</ul></li>
<li>Video<ul>
<li>
<label><input name="nestchoice" type="checkbox" value="vhs"> VHS</label>
</li>
<li>
<label>
<input checked name="nestchoice" type="checkbox" value="dvd"> DVD
</label>
</li>
</ul></li>
</ul>
""" """
self.check_html(self.widget(choices=nested_choices), 'nestchoice', ('vinyl', 'dvd'), html=html) self.check_html(self.widget(choices=nested_choices), 'nestchoice', ('vinyl', 'dvd'), html=html)
@ -124,15 +103,15 @@ class CheckboxSelectMultipleTest(WidgetTest):
""" """
choices = [('a', 'A'), ('b', 'B'), ('c', 'C')] choices = [('a', 'A'), ('b', 'B'), ('c', 'C')]
html = """ html = """
<ul id="abc"> <div id="abc">
<li> <div>
<label for="abc_0"><input checked type="checkbox" name="letters" value="a" id="abc_0"> A</label> <label for="abc_0"><input checked type="checkbox" name="letters" value="a" id="abc_0"> A</label>
</li> </div>
<li><label for="abc_1"><input type="checkbox" name="letters" value="b" id="abc_1"> B</label></li> <div><label for="abc_1"><input type="checkbox" name="letters" value="b" id="abc_1"> B</label></div>
<li> <div>
<label for="abc_2"><input checked type="checkbox" name="letters" value="c" id="abc_2"> C</label> <label for="abc_2"><input checked type="checkbox" name="letters" value="c" id="abc_2"> C</label>
</li> </div>
</ul> </div>
""" """
self.check_html(self.widget(choices=choices), 'letters', ['a', 'c'], attrs={'id': 'abc'}, html=html) self.check_html(self.widget(choices=choices), 'letters', ['a', 'c'], attrs={'id': 'abc'}, html=html)
@ -142,15 +121,15 @@ class CheckboxSelectMultipleTest(WidgetTest):
""" """
widget = CheckboxSelectMultiple(attrs={'id': 'abc'}, choices=[('a', 'A'), ('b', 'B'), ('c', 'C')]) widget = CheckboxSelectMultiple(attrs={'id': 'abc'}, choices=[('a', 'A'), ('b', 'B'), ('c', 'C')])
html = """ html = """
<ul id="abc"> <div id="abc">
<li> <div>
<label for="abc_0"><input checked type="checkbox" name="letters" value="a" id="abc_0"> A</label> <label for="abc_0"><input checked type="checkbox" name="letters" value="a" id="abc_0"> A</label>
</li> </div>
<li><label for="abc_1"><input type="checkbox" name="letters" value="b" id="abc_1"> B</label></li> <div><label for="abc_1"><input type="checkbox" name="letters" value="b" id="abc_1"> B</label></div>
<li> <div>
<label for="abc_2"><input checked type="checkbox" name="letters" value="c" id="abc_2"> C</label> <label for="abc_2"><input checked type="checkbox" name="letters" value="c" id="abc_2"> C</label>
</li> </div>
</ul> </div>
""" """
self.check_html(widget, 'letters', ['a', 'c'], html=html) self.check_html(widget, 'letters', ['a', 'c'], html=html)
@ -162,11 +141,11 @@ class CheckboxSelectMultipleTest(WidgetTest):
(1000000, 'One million'), (1000000, 'One million'),
] ]
html = """ html = """
<ul> <div>
<li><label><input type="checkbox" name="numbers" value="1"> One</label></li> <div><label><input type="checkbox" name="numbers" value="1"> One</label></div>
<li><label><input type="checkbox" name="numbers" value="1000"> One thousand</label></li> <div><label><input type="checkbox" name="numbers" value="1000"> One thousand</label></div>
<li><label><input type="checkbox" name="numbers" value="1000000"> One million</label></li> <div><label><input type="checkbox" name="numbers" value="1000000"> One million</label></div>
</ul> </div>
""" """
self.check_html(self.widget(choices=choices), 'numbers', None, html=html) self.check_html(self.widget(choices=choices), 'numbers', None, html=html)
@ -175,10 +154,10 @@ class CheckboxSelectMultipleTest(WidgetTest):
(datetime.time(12, 0), 'noon'), (datetime.time(12, 0), 'noon'),
] ]
html = """ html = """
<ul> <div>
<li><label><input type="checkbox" name="times" value="00:00:00"> midnight</label></li> <div><label><input type="checkbox" name="times" value="00:00:00"> midnight</label></div>
<li><label><input type="checkbox" name="times" value="12:00:00"> noon</label></li> <div><label><input type="checkbox" name="times" value="12:00:00"> noon</label></div>
</ul> </div>
""" """
self.check_html(self.widget(choices=choices), 'times', None, html=html) self.check_html(self.widget(choices=choices), 'times', None, html=html)

View File

@ -11,15 +11,15 @@ class RadioSelectTest(WidgetTest):
def test_render(self): def test_render(self):
choices = (('', '------'),) + self.beatles choices = (('', '------'),) + self.beatles
self.check_html(self.widget(choices=choices), 'beatle', 'J', html=( self.check_html(self.widget(choices=choices), 'beatle', 'J', html="""
"""<ul> <div>
<li><label><input type="radio" name="beatle" value=""> ------</label></li> <div><label><input type="radio" name="beatle" value=""> ------</label></div>
<li><label><input checked type="radio" name="beatle" value="J"> John</label></li> <div><label><input checked type="radio" name="beatle" value="J"> John</label></div>
<li><label><input type="radio" name="beatle" value="P"> Paul</label></li> <div><label><input type="radio" name="beatle" value="P"> Paul</label></div>
<li><label><input type="radio" name="beatle" value="G"> George</label></li> <div><label><input type="radio" name="beatle" value="G"> George</label></div>
<li><label><input type="radio" name="beatle" value="R"> Ringo</label></li> <div><label><input type="radio" name="beatle" value="R"> Ringo</label></div>
</ul>""" </div>
)) """)
def test_nested_choices(self): def test_nested_choices(self):
nested_choices = ( nested_choices = (
@ -28,25 +28,23 @@ class RadioSelectTest(WidgetTest):
('Video', (('vhs', 'VHS'), ('dvd', 'DVD'))), ('Video', (('vhs', 'VHS'), ('dvd', 'DVD'))),
) )
html = """ html = """
<ul id="media"> <div id="media">
<li> <div>
<label for="media_0"><input id="media_0" name="nestchoice" type="radio" value="unknown"> Unknown</label> <label for="media_0"><input type="radio" name="nestchoice" value="unknown" id="media_0"> Unknown</label></div>
</li> <div>
<li>Audio<ul id="media_1"> <label>Audio</label>
<li> <div>
<label for="media_1_0"><input id="media_1_0" name="nestchoice" type="radio" value="vinyl"> Vinyl</label> <label for="media_1_0"><input type="radio" name="nestchoice" value="vinyl" id="media_1_0"> Vinyl</label></div>
</li> <div> <label for="media_1_1"><input type="radio" name="nestchoice" value="cd" id="media_1_1"> CD</label></div>
<li><label for="media_1_1"><input id="media_1_1" name="nestchoice" type="radio" value="cd"> CD</label></li> </div><div>
</ul></li> <label>Video</label>
<li>Video<ul id="media_2"> <div>
<li><label for="media_2_0"><input id="media_2_0" name="nestchoice" type="radio" value="vhs"> VHS</label></li> <label for="media_2_0"><input type="radio" name="nestchoice" value="vhs" id="media_2_0"> VHS</label></div>
<li> <div>
<label for="media_2_1"> <label for="media_2_1"><input type="radio" name="nestchoice" value="dvd" id="media_2_1" checked> DVD</label>
<input checked id="media_2_1" name="nestchoice" type="radio" value="dvd"> DVD </div>
</label> </div>
</li> </div>
</ul></li>
</ul>
""" """
self.check_html( self.check_html(
self.widget(choices=nested_choices), 'nestchoice', 'dvd', self.widget(choices=nested_choices), 'nestchoice', 'dvd',
@ -60,14 +58,14 @@ class RadioSelectTest(WidgetTest):
""" """
widget = RadioSelect(attrs={'id': 'foo'}, choices=self.beatles) widget = RadioSelect(attrs={'id': 'foo'}, choices=self.beatles)
html = """ html = """
<ul id="foo"> <div id="foo">
<li> <div>
<label for="foo_0"><input checked type="radio" id="foo_0" value="J" name="beatle"> John</label> <label for="foo_0"><input checked type="radio" id="foo_0" value="J" name="beatle"> John</label>
</li> </div>
<li><label for="foo_1"><input type="radio" id="foo_1" value="P" name="beatle"> Paul</label></li> <div><label for="foo_1"><input type="radio" id="foo_1" value="P" name="beatle"> Paul</label></div>
<li><label for="foo_2"><input type="radio" id="foo_2" value="G" name="beatle"> George</label></li> <div><label for="foo_2"><input type="radio" id="foo_2" value="G" name="beatle"> George</label></div>
<li><label for="foo_3"><input type="radio" id="foo_3" value="R" name="beatle"> Ringo</label></li> <div><label for="foo_3"><input type="radio" id="foo_3" value="R" name="beatle"> Ringo</label></div>
</ul> </div>
""" """
self.check_html(widget, 'beatle', 'J', html=html) self.check_html(widget, 'beatle', 'J', html=html)
@ -77,29 +75,29 @@ class RadioSelectTest(WidgetTest):
inputs. inputs.
""" """
html = """ html = """
<ul id="bar"> <div id="bar">
<li> <div>
<label for="bar_0"><input checked type="radio" id="bar_0" value="J" name="beatle"> John</label> <label for="bar_0"><input checked type="radio" id="bar_0" value="J" name="beatle"> John</label>
</li> </div>
<li><label for="bar_1"><input type="radio" id="bar_1" value="P" name="beatle"> Paul</label></li> <div><label for="bar_1"><input type="radio" id="bar_1" value="P" name="beatle"> Paul</label></div>
<li><label for="bar_2"><input type="radio" id="bar_2" value="G" name="beatle"> George</label></li> <div><label for="bar_2"><input type="radio" id="bar_2" value="G" name="beatle"> George</label></div>
<li><label for="bar_3"><input type="radio" id="bar_3" value="R" name="beatle"> Ringo</label></li> <div><label for="bar_3"><input type="radio" id="bar_3" value="R" name="beatle"> Ringo</label></div>
</ul> </div>
""" """
self.check_html(self.widget(choices=self.beatles), 'beatle', 'J', attrs={'id': 'bar'}, html=html) self.check_html(self.widget(choices=self.beatles), 'beatle', 'J', attrs={'id': 'bar'}, html=html)
def test_class_attrs(self): def test_class_attrs(self):
""" """
The <ul> in the multiple_input.html widget template include the class The <div> in the multiple_input.html widget template include the class
attribute. attribute.
""" """
html = """ html = """
<ul class="bar"> <div class="bar">
<li><label><input checked type="radio" class="bar" value="J" name="beatle"> John</label></li> <div><label><input checked type="radio" class="bar" value="J" name="beatle"> John</label></div>
<li><label><input type="radio" class="bar" value="P" name="beatle"> Paul</label></li> <div><label><input type="radio" class="bar" value="P" name="beatle"> Paul</label></div>
<li><label><input type="radio" class="bar" value="G" name="beatle"> George</label></li> <div><label><input type="radio" class="bar" value="G" name="beatle"> George</label></div>
<li><label><input type="radio" class="bar" value="R" name="beatle"> Ringo</label></li> <div><label><input type="radio" class="bar" value="R" name="beatle"> Ringo</label></div>
</ul> </div>
""" """
self.check_html(self.widget(choices=self.beatles), 'beatle', 'J', attrs={'class': 'bar'}, html=html) self.check_html(self.widget(choices=self.beatles), 'beatle', 'J', attrs={'class': 'bar'}, html=html)
@ -111,11 +109,11 @@ class RadioSelectTest(WidgetTest):
(1000000, 'One million'), (1000000, 'One million'),
] ]
html = """ html = """
<ul> <div>
<li><label><input type="radio" name="number" value="1"> One</label></li> <div><label><input type="radio" name="number" value="1"> One</label></div>
<li><label><input type="radio" name="number" value="1000"> One thousand</label></li> <div><label><input type="radio" name="number" value="1000"> One thousand</label></div>
<li><label><input type="radio" name="number" value="1000000"> One million</label></li> <div><label><input type="radio" name="number" value="1000000"> One million</label></div>
</ul> </div>
""" """
self.check_html(self.widget(choices=choices), 'number', None, html=html) self.check_html(self.widget(choices=choices), 'number', None, html=html)
@ -124,22 +122,22 @@ class RadioSelectTest(WidgetTest):
(datetime.time(12, 0), 'noon'), (datetime.time(12, 0), 'noon'),
] ]
html = """ html = """
<ul> <div>
<li><label><input type="radio" name="time" value="00:00:00"> midnight</label></li> <div><label><input type="radio" name="time" value="00:00:00"> midnight</label></div>
<li><label><input type="radio" name="time" value="12:00:00"> noon</label></li> <div><label><input type="radio" name="time" value="12:00:00"> noon</label></div>
</ul> </div>
""" """
self.check_html(self.widget(choices=choices), 'time', None, html=html) self.check_html(self.widget(choices=choices), 'time', None, html=html)
def test_render_as_subwidget(self): def test_render_as_subwidget(self):
"""A RadioSelect as a subwidget of MultiWidget.""" """A RadioSelect as a subwidget of MultiWidget."""
choices = (('', '------'),) + self.beatles choices = (('', '------'),) + self.beatles
self.check_html(MultiWidget([self.widget(choices=choices)]), 'beatle', ['J'], html=( self.check_html(MultiWidget([self.widget(choices=choices)]), 'beatle', ['J'], html="""
"""<ul> <div>
<li><label><input type="radio" name="beatle_0" value=""> ------</label></li> <div><label><input type="radio" name="beatle_0" value=""> ------</label></div>
<li><label><input checked type="radio" name="beatle_0" value="J"> John</label></li> <div><label><input checked type="radio" name="beatle_0" value="J"> John</label></div>
<li><label><input type="radio" name="beatle_0" value="P"> Paul</label></li> <div><label><input type="radio" name="beatle_0" value="P"> Paul</label></div>
<li><label><input type="radio" name="beatle_0" value="G"> George</label></li> <div><label><input type="radio" name="beatle_0" value="G"> George</label></div>
<li><label><input type="radio" name="beatle_0" value="R"> Ringo</label></li> <div><label><input type="radio" name="beatle_0" value="R"> Ringo</label></div>
</ul>""" </div>
)) """)

View File

@ -294,14 +294,14 @@ class ModelChoiceFieldTests(TestCase):
field = CustomModelMultipleChoiceField(Category.objects.all()) field = CustomModelMultipleChoiceField(Category.objects.all())
self.assertHTMLEqual( self.assertHTMLEqual(
field.widget.render('name', []), ( field.widget.render('name', []), (
'<ul>' '<div>'
'<li><label><input type="checkbox" name="name" value="%d" ' '<div><label><input type="checkbox" name="name" value="%d" '
'data-slug="entertainment">Entertainment</label></li>' 'data-slug="entertainment">Entertainment</label></div>'
'<li><label><input type="checkbox" name="name" value="%d" ' '<div><label><input type="checkbox" name="name" value="%d" '
'data-slug="test">A test</label></li>' 'data-slug="test">A test</label></div>'
'<li><label><input type="checkbox" name="name" value="%d" ' '<div><label><input type="checkbox" name="name" value="%d" '
'data-slug="third-test">Third</label></li>' 'data-slug="third-test">Third</label></div>'
'</ul>' '</div>'
) % (self.c1.pk, self.c2.pk, self.c3.pk), ) % (self.c1.pk, self.c2.pk, self.c3.pk),
) )
@ -334,11 +334,11 @@ class ModelChoiceFieldTests(TestCase):
field = CustomModelMultipleChoiceField(Category.objects.all()) field = CustomModelMultipleChoiceField(Category.objects.all())
self.assertHTMLEqual( self.assertHTMLEqual(
field.widget.render('name', []), field.widget.render('name', []),
'''<ul> """<div>
<li><label><input type="checkbox" name="name" value="%d" data-slug="entertainment">Entertainment</label></li> <div><label><input type="checkbox" name="name" value="%d" data-slug="entertainment">Entertainment</label></div>
<li><label><input type="checkbox" name="name" value="%d" data-slug="test">A test</label></li> <div><label><input type="checkbox" name="name" value="%d" data-slug="test">A test</label></div>
<li><label><input type="checkbox" name="name" value="%d" data-slug="third-test">Third</label></li> <div><label><input type="checkbox" name="name" value="%d" data-slug="third-test">Third</label></div>
</ul>''' % (self.c1.pk, self.c2.pk, self.c3.pk), </div>""" % (self.c1.pk, self.c2.pk, self.c3.pk),
) )
def test_choices_not_fetched_when_not_rendering(self): def test_choices_not_fetched_when_not_rendering(self):