Skip to content

Commit

Permalink
Merge branch 'main' into govuk-frontend-50
Browse files Browse the repository at this point in the history
  • Loading branch information
matthew-shaw committed Feb 13, 2024
2 parents 9abf9f0 + 0dad144 commit 7febb7b
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 13 deletions.
2 changes: 1 addition & 1 deletion govuk_frontend_wtf/gov_form_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def map_gov_params(self, field, **kwargs):
"name": field.name,
"label": {"text": field.label.text},
"attributes": {},
"hint": {"text": field.description},
"hint": {"text": field.description} if field.description else None,
}

if "value" in kwargs:
Expand Down
16 changes: 12 additions & 4 deletions govuk_frontend_wtf/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,29 @@ def init_app(self, app):
def wtforms_errors(form, params={}):
wtforms_params = {"titleText": "There is a problem", "errorList": []}

wtforms_params["errorList"] = flatten_errors(form.errors)
id_map = {}
for field_name in form._fields.keys():
field = getattr(form, field_name, None)
if field and hasattr(field, "id"):
id_map[field_name] = field.id

wtforms_params["errorList"] = flatten_errors(form.errors, id_map=id_map)

return merger.merge(wtforms_params, params)


def flatten_errors(errors, prefix=""):
def flatten_errors(errors, prefix="", id_map={}):
"""Return list of errors from form errors."""
error_list = []
if isinstance(errors, dict):
for key, value in errors.items():
# Recurse to handle subforms.
error_list += flatten_errors(value, prefix=f"{prefix}{key}-")
if key in id_map:
key = id_map[key]
error_list += flatten_errors(value, prefix=f"{prefix}{key}-", id_map=id_map)
elif isinstance(errors, list) and isinstance(errors[0], dict):
for idx, error in enumerate(errors):
error_list += flatten_errors(error, prefix=f"{prefix}{idx}-")
error_list += flatten_errors(error, prefix=f"{prefix}{idx}-", id_map=id_map)
elif isinstance(errors, list):
error_list.append({"text": errors[0], "href": "#{}".format(prefix.rstrip("-"))})
else:
Expand Down
60 changes: 60 additions & 0 deletions tests/fixtures/wtf_widgets_data.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,44 @@ TestStringField:
- <div class="govuk-form-group govuk-form-group--error">
- <div id="string_field-hint" class="govuk-hint">\s*StringFieldHint\s*</div>

TestStringFieldId:
template: "{{ form.string_field_id }}"
tests:
test_empty_get:
expected_output:
- <input class="govuk-input" id="custom-id" name="string_field_id" type="text" aria-describedby="custom-id-hint" required="required">
- <label class="govuk-label" for="custom-id">\s*StringField\s*</label>
- <div id="custom-id-hint" class="govuk-hint">\s*StringFieldHint\s*</div>
test_output_sanitized:
request:
method: post
data:
string_field_id: <script>alert("Hello")</script>
expected_output:
- <input class="govuk-input govuk-input--error" id="custom-id" name="string_field_id" type="text" value="&lt;script&gt;alert\(&#34;Hello&#34;\)&lt;/script&gt;" aria-describedby="custom-id-hint custom-id-error" required="required">
not_expected_output:
- <script>alert("Hello")</script>
test_valid_post:
request:
method: post
data:
string_field_id: John Smith
expected_output:
- <input class="govuk-input" id="custom-id" name="string_field_id" type="text" value="John Smith" aria-describedby="custom-id-hint" required="required">
- <label class="govuk-label" for="custom-id">\s*StringField\s*</label>
- <div id="custom-id-hint" class="govuk-hint">\s*StringFieldHint\s*</div>
test_invalid_post:
request:
method: post
data:
string_field_id: foo
expected_output:
- <input class="govuk-input govuk-input--error" id="custom-id" name="string_field_id" type="text" value="foo" aria-describedby="custom-id-hint custom-id-error" required="required">
- <label class="govuk-label" for="custom-id">\s*StringField\s*</label>
- <p id="custom-id-error" class="govuk-error-message">\s*<span class="govuk-visually-hidden">\s*Error:\s*</span>\s*Example serverside error - type &#34;John Smith&#34; into this field to suppress it\s*</p>
- <div class="govuk-form-group govuk-form-group--error">
- <div id="custom-id-hint" class="govuk-hint">\s*StringFieldHint\s*</div>

TestDateField:
template: "{{ form.date_field }}"
tests:
Expand Down Expand Up @@ -585,6 +623,23 @@ TestRadioField:
- <input class="govuk-radios__input" id="radio_field-3" name="radio_field" type="radio" value="three">
- <label class="govuk-label govuk-radios__label" for="radio_field-3">\s*Three\s*</label>
- <div id="radio_field-hint" class="govuk-hint">\s*RadioFieldHint\s*</div>
test_empty_description:
request:
method: get
data:
radio_field_no_description: foo
expected_output:
- <div class="govuk-form-group">
- <div class="govuk-radios" data-module="govuk-radios">
- <div class="govuk-radios__item">
- <input class="govuk-radios__input" id="radio_field" name="radio_field" type="radio" value="one">
- <label class="govuk-label govuk-radios__label" for="radio_field">\s*One\s*</label>
- <input class="govuk-radios__input" id="radio_field-2" name="radio_field" type="radio" value="two">
- label class="govuk-label govuk-radios__label" for="radio_field-2">\s*Two\s*</label>
- <input class="govuk-radios__input" id="radio_field-3" name="radio_field" type="radio" value="three">
- <label class="govuk-label govuk-radios__label" for="radio_field-3">\s*Three\s*</label>
not_expected_output:
- <div id="radio_field-hint" class="govuk-hint"></div>

TestFileField:
template: "{{ form.file_field }}"
Expand Down Expand Up @@ -729,6 +784,7 @@ TestErrorSummary:
- <div role="alert">
- <h2 class="govuk-error-summary__title">\s*There is a problem\s*</h2>
- <a href="#string_field">StringField is required</a>
- <a href="#custom-id">StringField is required</a>
- <a href="#date_field">Date is required</a>
- <a href="#integer_field">IntegerField is required</a>
- <a href="#select_field">Please select an option</a>
Expand All @@ -749,6 +805,7 @@ TestErrorSummary:
method: post
data:
string_field: John Smith
string_field_id: John Smith
nested_form-0-string_field: John Smith
date_field:
- 1
Expand All @@ -770,6 +827,7 @@ TestErrorSummary:
- foo
- bar
radio_field: one
radio_field_no_description: one
select_multiple_field: one
textarea_field: foo
charactercount_field: foo
Expand All @@ -782,6 +840,7 @@ TestErrorSummary:
method: post
data:
string_field: Andy
string_field_id: Andy
date_field:
- 1
- 1
Expand All @@ -798,6 +857,7 @@ TestErrorSummary:
- foo
- bar
radio_field: one
radio_field_no_description: one
select_multiple_field: one
textarea_field: foo
charactercount_field: foo
Expand Down
19 changes: 19 additions & 0 deletions tests/fixtures/wtf_widgets_example_form.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@ class ExampleForm(FlaskForm):
description="StringFieldHint",
)

string_field_id = StringField(
"StringField",
id="custom-id",
widget=GovTextInput(),
validators=[InputRequired(message="StringField is required")],
description="StringFieldHint",
)

date_field = DateField(
"DateField",
format="%d %m %Y",
Expand Down Expand Up @@ -153,6 +161,13 @@ class ExampleForm(FlaskForm):
description="RadioFieldHint",
)

radio_field_no_description = RadioField(
"RadioField",
widget=GovRadioInput(),
validators=[InputRequired(message="Please select an option")],
choices=[("one", "One"), ("two", "Two"), ("three", "Three")],
)

file_field = FileField(
"FileField",
widget=GovFileInput(),
Expand Down Expand Up @@ -197,3 +212,7 @@ class ExampleForm(FlaskForm):
def validate_string_field(self, field):
if field.data != "John Smith":
raise ValidationError('Example serverside error - type "John Smith" into this field to suppress it')

def validate_string_field_id(self, field):
if field.data != "John Smith":
raise ValidationError('Example serverside error - type "John Smith" into this field to suppress it')
8 changes: 4 additions & 4 deletions tests/requirements.in
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
deepmerge==1.1.1
email_validator==2.1.0.post1
flask-wtf==1.2.1
flask==3.0.2
govuk-frontend-jinja==3.0.0
email_validator==2.0.0.post2
flask-wtf==1.1.2
flask==2.3.3
govuk-frontend-jinja==2.8.0
pytest-cov==4.1.0
pyyaml==6.0.1
8 changes: 4 additions & 4 deletions tests/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ blinker==1.7.0
# via flask
click==8.1.7
# via flask
coverage[toml]==7.4.1
coverage[toml]==7.2.7
# via
# coverage
# pytest-cov
Expand All @@ -18,13 +18,13 @@ dnspython==2.5.0
# via email-validator
email-validator==2.1.0.post1
# via -r requirements.in
flask==3.0.2
flask==2.3.3
# via
# -r requirements.in
# flask-wtf
flask-wtf==1.2.1
flask-wtf==1.1.2
# via -r requirements.in
govuk-frontend-jinja==3.0.0
govuk-frontend-jinja==2.8.0
# via -r requirements.in
idna==3.6
# via email-validator
Expand Down

0 comments on commit 7febb7b

Please sign in to comment.