Add a WTForms-compatible textbox macro

This macro:
- accepts a WTForm form field as a parameter
- renders a form field which follows the GOV.UK Elements patterns, both visually
  and in markup terms

It then changes any page which uses either:
- the old, non-WTForms macro or
- the old, WTFforms `render_field` macro

…to use this new macro and removes both of the old ones.

It also adds the option to display hint text above the textbox.
This commit is contained in:
Chris Hill-Scott
2016-01-11 13:15:10 +00:00
parent dd242752c7
commit e8fe8c50ba
21 changed files with 140 additions and 105 deletions

View File

@@ -22,6 +22,7 @@
@import '../govuk_elements/public/sass/elements/forms'; @import '../govuk_elements/public/sass/elements/forms';
@import '../govuk_elements/public/sass/elements/forms/form-validation'; @import '../govuk_elements/public/sass/elements/forms/form-validation';
@import '../govuk_elements/public/sass/elements/forms/form-block-labels'; @import '../govuk_elements/public/sass/elements/forms/form-block-labels';
@import '../govuk_elements/public/sass/elements/forms/form-validation';
@import '../govuk_elements/public/sass/elements/icons'; @import '../govuk_elements/public/sass/elements/icons';
@import '../govuk_elements/public/sass/elements/layout'; @import '../govuk_elements/public/sass/elements/layout';
@import '../govuk_elements/public/sass/elements/lists'; @import '../govuk_elements/public/sass/elements/lists';

View File

@@ -1,5 +1,5 @@
from flask_wtf import Form from flask_wtf import Form
from wtforms import StringField, PasswordField, ValidationError from wtforms import StringField, PasswordField, ValidationError, TextAreaField
from wtforms.validators import DataRequired, Email, Length, Regexp from wtforms.validators import DataRequired, Email, Length, Regexp
from app.main.validators import Blacklist, ValidateUserCodes from app.main.validators import Blacklist, ValidateUserCodes
@@ -121,6 +121,19 @@ class AddServiceForm(Form):
raise ValidationError('Service name already exists') raise ValidationError('Service name already exists')
class ServiceNameForm(Form):
service_name = StringField(u'New name')
class ConfirmPasswordForm(Form):
password = PasswordField(u'Enter password')
class TemplateForm(Form):
template_name = StringField(u'Template name')
template_body = TextAreaField(u'Message')
class ForgotPasswordForm(Form): class ForgotPasswordForm(Form):
email_address = email_address() email_address = email_address()

View File

@@ -2,6 +2,7 @@ from flask import render_template, redirect, request, url_for, abort
from flask_login import login_required from flask_login import login_required
from app.main import main from app.main import main
from app.main.forms import ConfirmPasswordForm, ServiceNameForm
service = { service = {
'name': 'Service name', 'name': 'Service name',
@@ -20,10 +21,15 @@ def service_settings():
@main.route("/service-settings/name", methods=['GET', 'POST']) @main.route("/service-settings/name", methods=['GET', 'POST'])
def name(): def name():
form = ServiceNameForm()
form.service_name.data = 'Service name'
if request.method == 'GET': if request.method == 'GET':
return render_template( return render_template(
'views/service-settings/name.html', 'views/service-settings/name.html',
service=service service=service,
form=form
) )
elif request.method == 'POST': elif request.method == 'POST':
return redirect(url_for('.confirm_name_change')) return redirect(url_for('.confirm_name_change'))
@@ -31,10 +37,14 @@ def name():
@main.route("/service-settings/name/confirm", methods=['GET', 'POST']) @main.route("/service-settings/name/confirm", methods=['GET', 'POST'])
def confirm_name_change(): def confirm_name_change():
form = ConfirmPasswordForm()
if request.method == 'GET': if request.method == 'GET':
return render_template( return render_template(
'views/service-settings/confirm.html', 'views/service-settings/confirm.html',
heading='Change your service name' heading='Change your service name',
form=form
) )
elif request.method == 'POST': elif request.method == 'POST':
return redirect(url_for('.service_settings')) return redirect(url_for('.service_settings'))
@@ -64,11 +74,15 @@ def status():
@main.route("/service-settings/status/confirm", methods=['GET', 'POST']) @main.route("/service-settings/status/confirm", methods=['GET', 'POST'])
def confirm_status_change(): def confirm_status_change():
form = ConfirmPasswordForm()
if request.method == 'GET': if request.method == 'GET':
return render_template( return render_template(
'views/service-settings/confirm.html', 'views/service-settings/confirm.html',
heading='Turn off all outgoing notifications', heading='Turn off all outgoing notifications',
destructive=True destructive=True,
form=form
) )
elif request.method == 'POST': elif request.method == 'POST':
return redirect(url_for('.service_settings')) return redirect(url_for('.service_settings'))
@@ -87,11 +101,15 @@ def delete():
@main.route("/service-settings/delete/confirm", methods=['GET', 'POST']) @main.route("/service-settings/delete/confirm", methods=['GET', 'POST'])
def confirm_delete(): def confirm_delete():
form = ConfirmPasswordForm()
if request.method == 'GET': if request.method == 'GET':
return render_template( return render_template(
'views/service-settings/confirm.html', 'views/service-settings/confirm.html',
heading='Delete this service from Notify', heading='Delete this service from Notify',
destructive=True destructive=True,
form=form
) )
elif request.method == 'POST': elif request.method == 'POST':
return redirect(url_for('.dashboard')) return redirect(url_for('.dashboard'))

View File

@@ -1,7 +1,24 @@
from flask import render_template from flask import render_template
from flask_wtf import Form
from wtforms import StringField, PasswordField, TextAreaField, validators
from app.main import main from app.main import main
@main.route('/_styleguide') @main.route('/_styleguide')
def styleguide(): def styleguide():
return render_template('views/styleguide.html')
class FormExamples(Form):
username = StringField(u'Username')
password = PasswordField(u'Password', [validators.required()])
message = TextAreaField(u'Message')
form = FormExamples()
form.message.data = "Your vehicle tax for ((registration number)) is due on ((date)). Renew online at www.gov.uk/vehicle-tax" # noqa
form.validate()
return render_template(
'views/styleguide.html',
form=form
)

View File

@@ -1,6 +1,7 @@
from flask import request, render_template, redirect, url_for from flask import request, render_template, redirect, url_for
from app.main import main from app.main import main
from app.main.forms import TemplateForm
@main.route("/templates") @main.route("/templates")
@@ -10,12 +11,17 @@ def manage_templates():
@main.route("/templates/template", methods=['GET', 'POST']) @main.route("/templates/template", methods=['GET', 'POST'])
def add_template(): def add_template():
form = TemplateForm()
form.template_name.data = 'Reminder'
form.template_body.data = 'Vehicle tax: Your vehicle tax for ((registration number)) expires on ((date)). Tax your vehicle at www.gov.uk/vehicle-tax' # noqa
if request.method == 'GET': if request.method == 'GET':
return render_template( return render_template(
'views/edit-template.html', 'views/edit-template.html',
template_name='Reminder', h1='Edit template',
template_body='Vehicle tax: Your vehicle tax for ((registration number)) expires on ((date)). Tax your vehicle at www.gov.uk/vehicle-tax', # noqa form=form
h1='Edit template'
) )
elif request.method == 'POST': elif request.method == 'POST':
return redirect(url_for('.manage_templates')) return redirect(url_for('.manage_templates'))
@@ -23,10 +29,14 @@ def add_template():
@main.route("/templates/template/add", methods=['GET', 'POST']) @main.route("/templates/template/add", methods=['GET', 'POST'])
def edit_template(): def edit_template():
form = TemplateForm()
if request.method == 'GET': if request.method == 'GET':
return render_template( return render_template(
'views/edit-template.html', 'views/edit-template.html',
h1='Add template' h1='Add template',
form=form
) )
elif request.method == 'POST': elif request.method == 'POST':
return redirect(url_for('.manage_templates')) return redirect(url_for('.manage_templates'))

View File

@@ -1,4 +1,3 @@
{%- from "components/form-field.html" import render_field %}
{% extends "govuk_template.html" %} {% extends "govuk_template.html" %}
{% block head %} {% block head %}

View File

@@ -1,12 +0,0 @@
{% macro render_field(field) %}
<dt>{{ field.label }}
<dd>{{ field(**kwargs)|safe }}
{% if field.errors %}
<ul class=error>
{% for error in field.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</dd>
{% endmacro %}

View File

@@ -1,15 +1,21 @@
{% macro textbox(name, label, value='', small=True, highlight_tags=False, password=False) %} {% macro textbox(field, hint=False, highlight_tags=False) %}
<div class="form-group"> <div class="form-group{% if field.errors %} error{% endif %}">
<label class="form-label" for="{{ name }}">{{ label }}</label> <label class="form-label" for="{{ field.name }}">
{% if small %} {{ field.label }}
<input class="form-control" id="{{ name }}" name="{{ name }}" type="{{ 'password' if password else 'text' }}" value="{{ value }}"> {% if hint %}
{% else %} <span class="form-hint">
<textarea {{ hint }}
class="form-control {% if highlight_tags %}textbox-highlight-textbox{% endif %}" </span>
id="{{ name }}" name="{{ name }}" {% endif %}
cols="30" rows="10" {% if field.errors %}
{% if highlight_tags %}data-module='highlight-tags'{% endif %} <span class="error-message">
>{{ value }}</textarea> {{ field.errors[0] }}
{% endif %} </span>
{% endif %}
</label>
{{ field(**{
'class': 'form-control textbox-highlight-textbox' if highlight_tags else 'form-control',
'data-module': 'highlight-tags' if highlight_tags else ''
}) }}
</div> </div>
{% endmacro %} {% endmacro %}

View File

@@ -1,5 +1,5 @@
{% extends "admin_template.html" %} {% extends "admin_template.html" %}
{% from "components/form-field.html" import render_field %} {% from "components/textbox.html" import textbox %}
{% block page_title %} {% block page_title %}
GOV.UK Notify | Set up service GOV.UK Notify | Set up service
@@ -17,9 +17,8 @@ GOV.UK Notify | Set up service
<li>as your email sender name</li> <li>as your email sender name</li>
</ul> </ul>
<form autocomplete="off" action="" method="post"> <form autocomplete="off" method="post">
{{ form.hidden_tag() }} {{ textbox(form.service_name) }}
{{ render_field(form.service_name, class='form-control-2-3') }}
<p> <p>
<button class="button" href="dashboard" role="button">Continue</button> <button class="button" href="dashboard" role="button">Continue</button>

View File

@@ -11,8 +11,8 @@ GOV.UK Notify | Edit template
<h1 class="heading-xlarge">{{ h1 }}</h1> <h1 class="heading-xlarge">{{ h1 }}</h1>
<form method="post"> <form method="post">
{{ textbox(name='template_name', label='Template name', value=template_name) }} {{ textbox(form.template_name) }}
{{ textbox(name='template_body', label='Message', small=False, value=template_body, highlight_tags=True) }} {{ textbox(form.template_body, highlight_tags=True) }}
{{ page_footer( {{ page_footer(
'Save and continue', 'Save and continue',
back_link=url_for('.dashboard'), back_link=url_for('.dashboard'),

View File

@@ -1,4 +1,5 @@
{% extends "admin_template.html" %} {% extends "admin_template.html" %}
{% from "components/textbox.html" import textbox %}
{% from "components/page-footer.html" import page_footer %} {% from "components/page-footer.html" import page_footer %}
{% block page_title %} {% block page_title %}
@@ -14,13 +15,10 @@ GOV.UK Notify
<p>Check your email address is correct and then resend the confirmation code.</p> <p>Check your email address is correct and then resend the confirmation code.</p>
<p> <p>
</p> </p>
<form autocomplete="off" action="" method="post"> <form autocomplete="off" method="post">
{{ form.hidden_tag() }} {{ textbox(form.email_address) }}
{{ render_field(form.email_address, class='form-control-2-3') }} <span class="font-xsmall">Your email address must end in .gov.uk</span>
<span class="font-xsmall">Your email address must end in .gov.uk</span> {{ page_footer('Resend confirmation code') }}
<p>
</p>
{{ page_footer('Resend confirmation code') }}
</form> </form>
</div> </div>
</div> </div>

View File

@@ -1,4 +1,6 @@
{% extends "admin_template.html" %} {% extends "admin_template.html" %}
{% from "components/textbox.html" import textbox %}
{% from "components/page-footer.html" import page_footer %}
{% block page_title %} {% block page_title %}
GOV.UK Notify GOV.UK Notify
@@ -12,13 +14,11 @@ GOV.UK Notify
<p>If you have forgotten your password, we can send you an email to create a new password.</p> <p>If you have forgotten your password, we can send you an email to create a new password.</p>
<form autocomplete="off" action="" method="post"> <form autocomplete="off" method="post">
{{ form.hidden_tag() }} {{ textbox(form.email_address) }}
{{ render_field(form.email_address, class='form-control-2-3') }} {{ page_footer("Send email") }}
<p>
<button class="button" role="button">Send email</button>
</p>
</form> </form>
</div> </div>
</div> </div>

View File

@@ -1,4 +1,6 @@
{% extends "admin_template.html" %} {% extends "admin_template.html" %}
{% from "components/textbox.html" import textbox %}
{% from "components/page-footer.html" import page_footer %}
{% block page_title %} {% block page_title %}
GOV.UK Notify GOV.UK Notify
@@ -13,17 +15,9 @@ GOV.UK Notify
<p> You can now create a new password for your account.</p> <p> You can now create a new password for your account.</p>
<form action="" autocomplete="off" method="post"> <form method="post" autocomplete="off">
{{ form.hidden_tag() }} {{ textbox(form.new_password, hint="Your password must have at least 10 characters") }}
<p> {{ page_footer("Continue") }}
{{ render_field(form.new_password, class="form-control-1-4", type="password") }}
<span class="font-xsmall">Your password must have at least 10 characters</span>
</p>
<p>
<button class="button" role="button">Continue</button>
</p>
</form> </form>
{% else %} {% else %}
Message about email address does not exist. Some one needs to figure out the words here. Message about email address does not exist. Some one needs to figure out the words here.

View File

@@ -1,4 +1,5 @@
{% extends "admin_template.html" %} {% extends "admin_template.html" %}
{% from "components/textbox.html" import textbox %}
{% from "components/page-footer.html" import page_footer %} {% from "components/page-footer.html" import page_footer %}
{% block page_title %} {% block page_title %}
@@ -16,12 +17,10 @@ GOV.UK Notify | Create an account
<form autocomplete="off" action="" method="post"> <form autocomplete="off" action="" method="post">
{{ form.hidden_tag() }} {{ form.hidden_tag() }}
{{ render_field(form.name, class='form-control-2-3') }} {{ textbox(form.name) }}
{{ render_field(form.email_address, class='form-control-2-3') }} {{ textbox(form.email_address, hint="Your email address must end in .gov.uk") }}
<span class="font-xsmall">Your email address must end in .gov.uk</span> {{ textbox(form.mobile_number) }}
{{ render_field(form.mobile_number, class='form-control-2-3') }} {{ textbox(form.password, hint="Your password must have at least 10 characters") }}
{{ render_field(form.password, class='form-control-2-3') }}
<span class="font-xsmall">Your password must have at least 10 characters</span></label>
{{ page_footer("Continue") }} {{ page_footer("Continue") }}
</form> </form>
</div> </div>

View File

@@ -14,7 +14,7 @@ GOV.UK Notify | Service settings
<div class="column-three-quarters"> <div class="column-three-quarters">
<form method="post"> <form method="post">
{{ textbox('new_name', 'Enter your password', password=True) }} {{ textbox(form.password) }}
{{ page_footer( {{ page_footer(
'Confirm', 'Confirm',
destructive=destructive, destructive=destructive,

View File

@@ -13,12 +13,15 @@ GOV.UK Notify | Service settings
<div class="grid-row"> <div class="grid-row">
<div class="column-three-quarters"> <div class="column-three-quarters">
<p> <p>Users will see your service name:</p>
Your service name ({{ service.name }}) is included in every sent notification
</p> <ul class="list-bullet">
<li>at the start of every text message, eg Vehicle tax: we received your payment, thank you</li>
<li>as your email sender name</li>
</ul>
<form method="post"> <form method="post">
{{ textbox('new_name', 'New name', value=service.name) }} {{ textbox(form.service_name) }}
{{ page_footer( {{ page_footer(
'Save', 'Save',
back_link=url_for('.service_settings') back_link=url_for('.service_settings')

View File

@@ -1,4 +1,5 @@
{% extends "admin_template.html" %} {% extends "admin_template.html" %}
{% from "components/textbox.html" import textbox %}
{% from "components/page-footer.html" import page_footer %} {% from "components/page-footer.html" import page_footer %}
{% block page_title %} {% block page_title %}
@@ -13,14 +14,10 @@ Sign in
<p>If you do not have an account, you can <a href="register">register for one now</a>.</p> <p>If you do not have an account, you can <a href="register">register for one now</a>.</p>
<form autocomplete="off" action="" method="post"> <form autocomplete="off" method="post">
{{ form.hidden_tag() }} {{ textbox(form.email_address) }}
{{ render_field(form.email_address, class='form-control-2-3') }} {{ textbox(form.password) }}
{{ render_field(form.password, class='form-control-2-3') }} {{ page_footer("Continue", back_link="#", back_link_text="Forgotten password?") }}
<p>
<span class="font-xsmall"><a href="{{url_for('main.forgot_password')}}">Forgotten password?</a></span>
</p>
{{ page_footer("Continue") }}
</form> </form>
</div> </div>
</div> </div>

View File

@@ -148,15 +148,8 @@
{% endcall %} {% endcall %}
<h2 class="heading-large">Textbox</h2> <h2 class="heading-large">Textbox</h2>
{{ textbox(form.username) }}
{{ textbox('name', 'Username') }} {{ textbox(form.password) }}
{{ textbox('password', 'Password', password=True) }} {{ textbox(form.message, highlight_tags=True) }}
{{ textbox(
'message',
"Message",
value="Your vehicle tax for ((registration number)) is due on ((date)). Renew online at www.gov.uk/vehicle-tax",
small=False,
highlight_tags=True
) }}
{% endblock %} {% endblock %}

View File

@@ -1,4 +1,5 @@
{% extends "admin_template.html" %} {% extends "admin_template.html" %}
{% from "components/textbox.html" import textbox %}
{% from "components/page-footer.html" import page_footer %} {% from "components/page-footer.html" import page_footer %}
{% block page_title %} {% block page_title %}
@@ -13,9 +14,8 @@ GOV.UK Notify
<p>Check your mobile phone number is correct and then resend the confirmation code.</p> <p>Check your mobile phone number is correct and then resend the confirmation code.</p>
<form autocomplete="off" action="" method="post"> <form autocomplete="off" method="post">
{{ form.hidden_tag() }} {{ textbox(form.mobile_number) }}
{{ render_field(form.mobile_number, class='form-control-2-3') }}
{{ page_footer("Resend confirmation code") }} {{ page_footer("Resend confirmation code") }}
</form> </form>
</div> </div>

View File

@@ -1,4 +1,5 @@
{% extends "admin_template.html" %} {% extends "admin_template.html" %}
{% from "components/textbox.html" import textbox %}
{% from "components/page-footer.html" import page_footer %} {% from "components/page-footer.html" import page_footer %}
{% block page_title %} {% block page_title %}
@@ -14,9 +15,8 @@ GOV.UK Notify | Text verification
<p>We've sent you a text message with a verification code.</p> <p>We've sent you a text message with a verification code.</p>
<form autocomplete="off" action="" method="post"> <form autocomplete="off" method="post">
{{ form.hidden_tag() }} {{ textbox(form.sms_code) }}
{{ render_field(form.sms_code, class='form-control-1-4') }}
<span class="font-xsmall"><a href="{{ url_for('.verification_code_not_received') }}">I haven't received a text</a></span> <span class="font-xsmall"><a href="{{ url_for('.verification_code_not_received') }}">I haven't received a text</a></span>
{{ page_footer("Continue") }} {{ page_footer("Continue") }}
</form> </form>

View File

@@ -1,4 +1,5 @@
{% extends "admin_template.html" %} {% extends "admin_template.html" %}
{% from "components/textbox.html" import textbox %}
{% from "components/page-footer.html" import page_footer %} {% from "components/page-footer.html" import page_footer %}
{% block page_title %} {% block page_title %}
@@ -13,11 +14,10 @@ GOV.UK Notify | Confirm email address and mobile number
<p>We've sent you confirmation codes by email and text message. You need to enter both codes here.</p> <p>We've sent you confirmation codes by email and text message. You need to enter both codes here.</p>
<form autocomplete="off" action="" method="post"> <form autocomplete="off" method="post">
{{ form.hidden_tag() }} {{ textbox(form.email_code) }}
{{ render_field(form.email_code, class='form-control-1-4') }}
<span class="font-xsmall"><a href="{{ url_for('.check_and_resend_email_code')}}">I haven't received an email</a></span> <span class="font-xsmall"><a href="{{ url_for('.check_and_resend_email_code')}}">I haven't received an email</a></span>
{{ render_field(form.sms_code, class='form-control-1-4') }} {{ textbox(form.sms_code) }}
<span class="font-xsmall"><a href="{{ url_for('.check_and_resend_text_code') }}">I haven't received a text</a></span> <span class="font-xsmall"><a href="{{ url_for('.check_and_resend_text_code') }}">I haven't received a text</a></span>
{{ page_footer("Continue") }} {{ page_footer("Continue") }}
</form> </form>