Skip to content

Commit

Permalink
Merge pull request #100 from sedrubal/fix-checkbox
Browse files Browse the repository at this point in the history
Adapt checkbox rendering for Bootstrap4
  • Loading branch information
dyve committed Aug 22, 2018
2 parents c513e0f + 5cdeb69 commit d346ac1
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 12 deletions.
29 changes: 20 additions & 9 deletions bootstrap4/renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,8 @@ def add_class_attrs(self, widget=None):
classes = add_css_class(classes, "form-control", prepend=True)
# For these widget types, add the size class here
classes = add_css_class(classes, self.get_size_class())
elif isinstance(widget, CheckboxInput):
classes = add_css_class(classes, "form-check-input", prepend=True)

if self.field.errors:
if self.error_css_class:
Expand Down Expand Up @@ -392,13 +394,11 @@ def list_to_class(self, html, klass):
return html

def add_checkbox_label(self, html):
return "{field}{label}".format(
field=html,
label=render_label(
content=mark_safe(self.field.label),
label_for=self.field.id_for_label,
label_title=escape(strip_tags(self.field_help)),
),
return html + render_label(
content=self.field.label,
label_for=self.field.id_for_label,
label_title=escape(strip_tags(self.field_help)),
label_class="form-check-label",
)

def fix_date_select_input(self, html):
Expand Down Expand Up @@ -445,7 +445,7 @@ def wrap_widget(self, html):
if isinstance(self.widget, CheckboxInput):
# Wrap checkboxes
# Note checkboxes do not get size classes, see #318
html = '<div class="checkbox">{content}</div>'.format(content=html)
html = '<div class="form-check">{content}</div>'.format(content=html)
return html

def make_input_group_addon(self, inner_class, outer_class, content):
Expand Down Expand Up @@ -486,7 +486,7 @@ def make_input_group(self, html):
)
return html

def append_to_field(self, html):
def append_help_and_error(self, html):
field_help = self.field_help or None
field_errors = self.field_errors
if field_help or field_errors:
Expand All @@ -503,6 +503,16 @@ def append_to_field(self, html):
html += help_html
return html

def append_to_field(self, html):
if isinstance(self.widget, CheckboxInput):
return html
return self.append_help_and_error(html)

def append_to_checkbox_field(self, html):
if not isinstance(self.widget, CheckboxInput):
return html
return self.append_help_and_error(html)

def get_field_class(self):
field_class = self.field_class
if not field_class and self.layout == "horizontal":
Expand Down Expand Up @@ -581,6 +591,7 @@ def _render(self):
self.restore_widget_attrs()
# Start post render
html = self.post_widget_render(html)
html = self.append_to_checkbox_field(html)
html = self.wrap_widget(html)
html = self.make_input_group(html)
html = self.append_to_field(html)
Expand Down
64 changes: 61 additions & 3 deletions tests/test_templatetags.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from __future__ import unicode_literals

import re

from bs4 import BeautifulSoup
from django import forms
from django.contrib.admin.widgets import AdminSplitDateTime
from django.contrib.gis import forms as gisforms
Expand Down Expand Up @@ -441,6 +443,62 @@ def test_password(self):
self.assertIn('type="password"', res)
self.assertIn('placeholder="Password"', res)

def test_checkbox(self):
"""Test Checkbox rendering, because it is special."""
def _select_one_element(html, selector, err_msg):
lst = html.select(selector)
self.assertEqual(len(lst), 1, err_msg)
return lst[0]

res = render_form_field("cc_myself")
# strip out newlines and spaces around newlines
res = "".join(line.strip() for line in res.split('\n'))
res = BeautifulSoup(res, 'html.parser')
form_group = _select_one_element(
res,
".form-group",
"Checkbox should be rendered inside a .form-group.",
)
form_check = _select_one_element(
form_group,
".form-check",
"There should be a .form-check inside .form-group",
)
checkbox = _select_one_element(
form_check,
"input",
"The checkbox should be inside the .form-check",
)
self.assertIn(
"form-check-input",
checkbox["class"],
"The checkbox should have the class 'form-check-input'.",
)
label = checkbox.nextSibling
self.assertIsNotNone(label, "The label should be rendered after the checkbox.")
self.assertEqual(label.name, "label", "After the checkbox there should be a label.")
self.assertEqual(
label["for"],
checkbox["id"],
"The for attribute of the label should be the id of the checkbox.",
)
help_text = label.nextSibling
self.assertIsNotNone(help_text, "The help text should be rendered after the label.")
self.assertEqual(
help_text.name,
"small", "The help text should be rendered as <small> tag.",
)
self.assertIn(
"form-text",
help_text["class"],
"The help text should have the class 'form-text'.",
)
self.assertIn(
"text-muted",
help_text["class"],
"The help text should have the class 'text-muted'.",
)

def test_required_field(self):
required_css_class = "bootstrap4-req"
required_field = render_form_field("subject")
Expand Down Expand Up @@ -718,13 +776,13 @@ def test_for_formset(self):

class PaginatorTest(TestCase):
def test_url_replace_param(self):
self.assertEquals(
self.assertEqual(
url_replace_param("/foo/bar?baz=foo", "baz", "yohoo"), "/foo/bar?baz=yohoo"
)
self.assertEquals(
self.assertEqual(
url_replace_param("/foo/bar?baz=foo", "baz", None), "/foo/bar"
)
self.assertEquals(
self.assertEqual(
url_replace_param("/foo/bar#id", "baz", "foo"), "/foo/bar?baz=foo#id"
)

Expand Down
1 change: 1 addition & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ commands =
coverage report -m
deps =
coverage
beautifulsoup4
1.11: Django>=1.11,<2.0
2.0: Django>=2.0,<2.1
2.1: Django==2.1
Expand Down

0 comments on commit d346ac1

Please sign in to comment.