Skip to content

Commit 94559cf

Browse files
authored
Merge pull request stephenmcd#206 from zbohm/fix-none-and-placeholder-in-dropdown
Fix None and placeholder in Dropdown input.
2 parents 4b7b065 + bf169ce commit 94559cf

File tree

2 files changed

+89
-9
lines changed

2 files changed

+89
-9
lines changed

forms_builder/forms/forms.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,12 @@ def __init__(self, form, context, *args, **kwargs):
159159
field_args["max_length"] = settings.FIELD_MAX_LENGTH
160160
if "choices" in arg_names:
161161
choices = list(field.get_choices())
162-
if (field.field_type == fields.SELECT and
163-
field.default not in [c[0] for c in choices]):
164-
choices.insert(0, ("", field.placeholder_text))
162+
if field.field_type == fields.SELECT and not (field.required and field.default):
163+
# The first OPTION with attr. value="" display only if...
164+
# 1. ...the field is not required.
165+
# 2. ...the field is required and the default is not set.
166+
text = "" if field.placeholder_text is None else field.placeholder_text
167+
choices.insert(0, ("", text))
165168
field_args["choices"] = choices
166169
if field_widget is not None:
167170
field_args["widget"] = field_widget
@@ -201,11 +204,15 @@ def __init__(self, form, context, *args, **kwargs):
201204
css_class = field_class.__name__.lower()
202205
if field.required:
203206
css_class += " required"
204-
if (settings.USE_HTML5 and
205-
field.field_type != fields.CHECKBOX_MULTIPLE):
206-
self.fields[field_key].widget.attrs["required"] = ""
207+
if settings.USE_HTML5:
208+
# Except Django version 1.10 this is necessary for all versions from 1.8 to 1.11.
209+
self.fields[field_key].widget.attrs["required"] = "required"
210+
207211
self.fields[field_key].widget.attrs["class"] = css_class
208-
if field.placeholder_text and not field.default:
212+
if field.placeholder_text and not field.default and field.field_type != fields.SELECT:
213+
# Attribute `placeholder` not allowed on element `select` at this point.
214+
# See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select
215+
# or check the code in https://validator.w3.org.
209216
text = field.placeholder_text
210217
self.fields[field_key].widget.attrs["placeholder"] = text
211218

forms_builder/forms/tests.py

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
from django.contrib.auth.models import User, AnonymousUser
55
from django.contrib.sites.models import Site
66
from django.db import IntegrityError
7-
from django.http import HttpResponseRedirect
7+
from django.http import HttpResponse, HttpResponseRedirect
88
from django.template import Context, RequestContext, Template
99
from django.test import TestCase
1010

11-
from forms_builder.forms.fields import NAMES, FILE
11+
from forms_builder.forms.fields import NAMES, FILE, SELECT
1212
from forms_builder.forms.forms import FormForForm
1313
from forms_builder.forms.models import (Form, Field,
1414
STATUS_DRAFT, STATUS_PUBLISHED)
@@ -158,3 +158,76 @@ def test_form_redirect(self):
158158
self.assertEqual(response["location"], redirect_url)
159159
response = self.client.post(form_absolute_url, {'field': 'bar'})
160160
self.assertFalse(isinstance(response, HttpResponseRedirect))
161+
162+
def test_input_dropdown_not_required(self):
163+
form = Form.objects.create(title="Test")
164+
form.fields.create(label="Foo", field_type=SELECT, required=False, choices="one, two, three")
165+
form_for_form = FormForForm(form, Context())
166+
self.assertContains(HttpResponse(form_for_form), """
167+
<select name="foo" class="choicefield" id="id_foo">
168+
<option value="" selected></option>
169+
<option value="one">one</option>
170+
<option value="two">two</option>
171+
<option value="three">three</option>
172+
</select>""", html=True)
173+
174+
def test_input_dropdown_not_required_with_placeholder(self):
175+
form = Form.objects.create(title="Test")
176+
form.fields.create(label="Foo", placeholder_text="choose item", field_type=SELECT,
177+
required=False, choices="one, two, three")
178+
form_for_form = FormForForm(form, Context())
179+
self.assertContains(HttpResponse(form_for_form), """
180+
<select name="foo" class="choicefield" id="id_foo">
181+
<option value="" selected>choose item</option>
182+
<option value="one">one</option>
183+
<option value="two">two</option>
184+
<option value="three">three</option>
185+
</select>""", html=True)
186+
187+
def test_input_dropdown_required(self):
188+
form = Form.objects.create(title="Test")
189+
form.fields.create(label="Foo", field_type=SELECT, choices="one, two, three")
190+
form_for_form = FormForForm(form, Context())
191+
self.assertContains(HttpResponse(form_for_form), """
192+
<select name="foo" required class="choicefield required" id="id_foo">
193+
<option value="" selected></option>
194+
<option value="one">one</option>
195+
<option value="two">two</option>
196+
<option value="three">three</option>
197+
</select>""", html=True)
198+
199+
def test_input_dropdown_required_with_placeholder(self):
200+
form = Form.objects.create(title="Test")
201+
form.fields.create(label="Foo", placeholder_text="choose item", field_type=SELECT,
202+
choices="one, two, three")
203+
form_for_form = FormForForm(form, Context())
204+
self.assertContains(HttpResponse(form_for_form), """
205+
<select name="foo" required class="choicefield required" id="id_foo">
206+
<option value="" selected>choose item</option>
207+
<option value="one">one</option>
208+
<option value="two">two</option>
209+
<option value="three">three</option>
210+
</select>""", html=True)
211+
212+
def test_input_dropdown_required_with_placeholder_and_default(self):
213+
form = Form.objects.create(title="Test")
214+
form.fields.create(label="Foo", placeholder_text="choose item", field_type=SELECT,
215+
choices="one, two, three", default="two")
216+
form_for_form = FormForForm(form, Context())
217+
self.assertContains(HttpResponse(form_for_form), """
218+
<select name="foo" required class="choicefield required" id="id_foo">
219+
<option value="one">one</option>
220+
<option value="two" selected>two</option>
221+
<option value="three">three</option>
222+
</select>""", html=True)
223+
224+
def test_input_dropdown_required_with_default(self):
225+
form = Form.objects.create(title="Test")
226+
form.fields.create(label="Foo", field_type=SELECT, choices="one, two, three", default="two")
227+
form_for_form = FormForForm(form, Context())
228+
self.assertContains(HttpResponse(form_for_form), """
229+
<select name="foo" required class="choicefield required" id="id_foo">
230+
<option value="one">one</option>
231+
<option value="two" selected>two</option>
232+
<option value="three">three</option>
233+
</select>""", html=True)

0 commit comments

Comments
 (0)