Browse Source

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

This improves accessibility for screen reader users.
David Smith 3 years ago
parent
commit
5942ab5eb1

+ 5 - 5
django/forms/jinja2/django/forms/widgets/multiple_input.html

@@ -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 %}
-  <li>{{ group }}<ul{% if id %} id="{{ id }}_{{ index }}"{% endif %}>{% endif %}{% for widget in options %}
-    <li>{% include widget.template_name %}</li>{% endfor %}{% if group %}
-  </ul></li>{% endif %}{% endfor %}
-</ul>
+{% 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 %}
+  <div><label>{{ group }}</label>{% endif %}{% for widget in options %}<div>
+    {% include widget.template_name %}</div>{% endfor %}{% if group %}
+  </div>{% endif %}{% endfor %}
+</div>

+ 5 - 5
django/forms/templates/django/forms/widgets/multiple_input.html

@@ -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 %}
-  <li>{{ group }}<ul{% if id %} id="{{ id }}_{{ index }}"{% endif %}>{% endif %}{% for option in options %}
-    <li>{% include option.template_name with widget=option %}</li>{% endfor %}{% if group %}
-  </ul></li>{% endif %}{% endfor %}
-</ul>{% endwith %}
+{% 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 %}
+  <div><label>{{ group }}</label>{% endif %}{% for option in options %}<div>
+    {% include option.template_name with widget=option %}</div>{% endfor %}{% if group %}
+  </div>{% endif %}{% endfor %}
+</div>{% endwith %}

+ 21 - 11
docs/ref/forms/widgets.txt

@@ -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'``
 
     Similar to :class:`Select`, but rendered as a list of radio buttons within
-    ``<li>`` tags:
+    ``<div>`` tags:
 
     .. code-block:: html
 
-        <ul>
-          <li><input type="radio" name="..."></li>
+        <div>
+          <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
     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>
 
     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
-    ``<li>`` tags, as above.
+    includes ``{{ myform.beatles }}`` -- they'll be output in a ``<div>`` with
+    ``<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.
 
     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
 
-        <ul>
-          <li><input type="checkbox" name="..." ></li>
+        <div>
+          <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.
 
+    .. 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
 widget's choices. Unlike :class:`RadioSelect`, the checkboxes won't include the
 ``required`` HTML attribute if the field is required because browser validation

+ 7 - 0
docs/releases/4.0.txt

@@ -551,6 +551,13 @@ Miscellaneous
   ``django.db.migrations.state.ProjectState.__init__()`` method must now be a
   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:
 
 Features deprecated in 4.0

+ 54 - 54
tests/forms_tests/tests/test_forms.py

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

+ 11 - 11
tests/forms_tests/tests/test_i18n.py

@@ -57,15 +57,15 @@ class FormsI18nTests(SimpleTestCase):
         self.assertHTMLEqual(
             f.as_p(),
             '<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> '
-            'En tied\xe4</label></li>\n'
-            '<li><label for="id_somechoice_1">'
+            'En tied\xe4</label></div>\n'
+            '<div><label for="id_somechoice_1">'
             '<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> '
-            'Nainen</label></li>\n</ul></p>'
+            'Nainen</label></div>\n</div></p>'
         )
 
         # Translated error messages
@@ -77,14 +77,14 @@ class FormsI18nTests(SimpleTestCase):
                 '\u041e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c'
                 '\u043d\u043e\u0435 \u043f\u043e\u043b\u0435.</li></ul>\n'
                 '<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> '
-                'En tied\xe4</label></li>\n'
-                '<li><label for="id_somechoice_1">'
+                'En tied\xe4</label></div>\n'
+                '<div><label for="id_somechoice_1">'
                 '<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> '
-                'Nainen</label></li>\n</ul></p>'
+                'Nainen</label></div>\n</div></p>'
             )
 
     def test_select_translated_text(self):

+ 77 - 98
tests/forms_tests/widget_tests/test_checkboxselectmultiple.py

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

+ 65 - 67
tests/forms_tests/widget_tests/test_radioselect.py

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

+ 13 - 13
tests/model_forms/test_modelchoicefield.py

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