Show form elements for templates and folders

This commit adds:
- checkboxes to let you select a template or folder
- radio buttons to let you select where to move those template(s) and/or
  folder(s) to

It only does the `get` part of this work; handling the `post` and
calling API will be done in a subsequent commit.
This commit is contained in:
Chris Hill-Scott
2018-11-12 08:37:46 +00:00
parent 921de47164
commit 2743216519
10 changed files with 215 additions and 31 deletions
@@ -23,3 +23,18 @@
#template-list { #template-list {
margin-top: $gutter; margin-top: $gutter;
} }
.template-list-item,
.template-list-item-with-checkbox {
position: relative;
}
.template-list-item-with-checkbox {
padding-left: $gutter * 2;
.multiple-choice {
position: absolute;
left: 0;
}
}
+45
View File
@@ -22,6 +22,7 @@ from wtforms import (
IntegerField, IntegerField,
PasswordField, PasswordField,
RadioField, RadioField,
SelectMultipleField,
StringField, StringField,
TextAreaField, TextAreaField,
ValidationError, ValidationError,
@@ -30,6 +31,7 @@ from wtforms import (
) )
from wtforms.fields.html5 import EmailField, SearchField, TelField from wtforms.fields.html5 import EmailField, SearchField, TelField
from wtforms.validators import URL, DataRequired, Length, Optional, Regexp from wtforms.validators import URL, DataRequired, Length, Optional, Regexp
from wtforms.widgets import CheckboxInput, ListWidget
from app.main.validators import ( from app.main.validators import (
Blacklist, Blacklist,
@@ -102,6 +104,11 @@ def get_next_days_until(until):
] ]
class MultiCheckboxField(SelectMultipleField):
widget = ListWidget(prefix_label=False)
option_widget = CheckboxInput()
def email_address(label='Email address', gov_user=True): def email_address(label='Email address', gov_user=True):
validators = [ validators = [
Length(min=5, max=255), Length(min=5, max=255),
@@ -1111,3 +1118,41 @@ class ReturnedLettersForm(StripWhitespaceForm):
class TemplateFolderForm(StripWhitespaceForm): class TemplateFolderForm(StripWhitespaceForm):
name = StringField('Folder name', validators=[DataRequired(message='Cant be empty')]) name = StringField('Folder name', validators=[DataRequired(message='Cant be empty')])
class TemplateAndFoldersSelectionForm(Form):
ALL_TEMPLATES_FOLDER = {
'name': 'All templates',
'id': None,
}
def __init__(
self,
service,
template_type,
current_folder_id,
*args,
**kwargs
):
super().__init__(*args, **kwargs)
self.templates_and_folders.choices = self.ids_and_names(
service.get_template_folders_and_templates(template_type, current_folder_id)
)
self.move_to.choices = self.ids_and_names(
[self.ALL_TEMPLATES_FOLDER] + service.all_template_folders,
exclude=current_folder_id,
)
@staticmethod
def ids_and_names(items, exclude=None):
return [
(item['id'], item['name']) for item in items
if item['id'] != exclude
]
templates_and_folders = MultiCheckboxField('Choose templates or folders')
move_to = RadioField('Choose a folder')
+10
View File
@@ -24,6 +24,7 @@ from app.main.forms import (
SearchTemplatesForm, SearchTemplatesForm,
SetTemplateSenderForm, SetTemplateSenderForm,
SMSTemplateForm, SMSTemplateForm,
TemplateAndFoldersSelectionForm,
TemplateFolderForm, TemplateFolderForm,
) )
from app.main.views.send import get_example_csv_rows, get_sender_details from app.main.views.send import get_example_csv_rows, get_sender_details
@@ -123,6 +124,10 @@ def choose_template(service_id, template_type='all', template_folder_id=None):
return render_template( return render_template(
'views/templates/choose.html', 'views/templates/choose.html',
current_template_folder_id=template_folder_id, current_template_folder_id=template_folder_id,
can_manage_folders=(
current_service.has_permission('edit_folders') and
current_user.has_permissions('manage_templates')
),
template_folder_path=current_service.get_template_folder_path(template_folder_id), template_folder_path=current_service.get_template_folder_path(template_folder_id),
template_folders=current_service.get_template_folders(template_folder_id), template_folders=current_service.get_template_folders(template_folder_id),
templates=current_service.get_templates(template_type, template_folder_id), templates=current_service.get_templates(template_type, template_folder_id),
@@ -134,6 +139,11 @@ def choose_template(service_id, template_type='all', template_folder_id=None):
template_nav_items=template_nav_items, template_nav_items=template_nav_items,
template_type=template_type, template_type=template_type,
search_form=SearchTemplatesForm(), search_form=SearchTemplatesForm(),
templates_and_folders_form=TemplateAndFoldersSelectionForm(
service=current_service,
template_type=template_type,
current_folder_id=template_folder_id,
)
) )
+6
View File
@@ -325,3 +325,9 @@ class Service():
path.append(folder) path.append(folder)
return list(reversed(path)) return list(reversed(path))
def get_template_folders_and_templates(self, template_type, template_folder_id):
return (
self.get_templates(template_type, template_folder_id) +
self.get_template_folders(template_folder_id)
)
+17 -6
View File
@@ -4,12 +4,7 @@
width='2-3' width='2-3'
) %} ) %}
<div class="multiple-choice"> <div class="multiple-choice">
<input {{ checkbox_input(field.id, field.name, field.data) }}
id="{{ field.id }}" name="{{ field.name }}" type="checkbox" value="y"
{% if field.data %}
checked
{% endif %}
>
<label for="{{ field.id }}"> <label for="{{ field.id }}">
{{ field.label.text }} {{ field.label.text }}
{% if hint %} {% if hint %}
@@ -22,6 +17,22 @@
{% endmacro %} {% endmacro %}
{% macro checkbox_input(id, name, data=None, value="y") %}
<input
id="{{ id }}" name="{{ name }}" type="checkbox" value="{{ value }}"
{% if data %}
checked
{% endif %}
>
{% endmacro %}
{% macro unlabelled_checkbox(id, name, data=None, value="y") %}
<div class="multiple-choice">
{{ checkbox_input(id, name, data, value) }}
<label></label>
</div>
{% endmacro %}
{% macro checkbox_group( {% macro checkbox_group(
legend, legend,
fields fields
+8 -1
View File
@@ -1,5 +1,7 @@
{% macro page_footer( {% macro page_footer(
button_text=None, button_text=None,
button_name=None,
button_value=None,
destructive=False, destructive=False,
back_link=False, back_link=False,
back_link_text="Back", back_link_text="Back",
@@ -11,7 +13,12 @@
<div class="page-footer"> <div class="page-footer">
{% if button_text %} {% if button_text %}
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" /> <input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<button type="submit" class="button{% if destructive %}-destructive{% endif %}">{{ button_text }}</button> <button
type="submit"
class="button{% if destructive %}-destructive{% endif %}"
>
{{- button_text -}}
</button>
{% endif %} {% endif %}
{% if back_link %} {% if back_link %}
<a class="page-footer-back-link" href="{{ back_link }}">{{ back_link_text }}</a> <a class="page-footer-back-link" href="{{ back_link }}">{{ back_link_text }}</a>
@@ -0,0 +1,7 @@
{% from "components/radios.html" import radios %}
{% from "components/page-footer.html" import page_footer %}
{% if templates_and_folders_form.move_to.choices %}
{{ radios(templates_and_folders_form.move_to) }}
{{ page_footer('Move selected', button_name='operation', button_value='move') }}
{% endif %}
@@ -0,0 +1,38 @@
{% from "components/checkbox.html" import unlabelled_checkbox %}
<nav id=template-list>
{% for template_folder in template_folders %}
<div class="template-list-item {% if can_manage_folders %}template-list-item-with-checkbox{% endif %}">
{% if can_manage_folders %}
{{ unlabelled_checkbox(
id='templates-or-folder-{}'.format(template_folder.id),
name='templates_and_folders',
value=template_folder.id,
) }}
{% endif %}
<h2 class="message-name">
<a href="{{ url_for('.choose_template', service_id=current_service.id, template_type=template_type, template_folder_id=template_folder.id) }}">
{{ template_folder.name }}
</a>
</h2>
<p class="message-type">Folder containing {{ template_count }} template{% if template_count != 1 %}s{% endif %}</p>
</div>
{% endfor %}
{% for template in templates %}
<div class="template-list-item {% if can_manage_folders %}template-list-item-with-checkbox{% endif %}">
{% if can_manage_folders %}
{{ unlabelled_checkbox(
id='templates-or-folder-{}'.format(template.id),
name='templates_and_folders',
value=template.id,
) }}
{% endif %}
<h2 class="message-name">
<a href="{{ url_for('.view_template', service_id=current_service.id, template_id=template.id) }}">{{ template.name }}</a>
</h2>
<p class="message-type">
{{ message_count_label(1, template.template_type, suffix='')|capitalize }} template
</p>
</div>
{% endfor %}
</nav>
+15 -23
View File
@@ -75,30 +75,22 @@
</div> </div>
{% endif %} {% endif %}
{{ live_search(target_selector='#template-list .column-whole', show=show_search_box, form=search_form) }} {{ live_search(target_selector='#template-list .template-list-item', show=show_search_box, form=search_form) }}
<nav class="grid-row" id=template-list> {% if can_manage_folders %}
{% for template_folder in template_folders %} <form method="post">
<div class="column-whole"> {% with templates=templates, template_folders=template_folders %}
<h2 class="message-name"> {% include 'views/templates/_template_list.html' %}
<a href="{{ url_for('.choose_template', service_id=current_service.id, template_type=template_type, template_folder_id=template_folder.id) }}"> {% endwith %}
{{ template_folder.name }} {% with templates_and_folders_form=templates_and_folders_form %}
</a> {% include 'views/templates/_move_to.html' %}
</h2> {% endwith %}
<p class="message-type">Folder containing {{ template_count }} template{% if template_count != 1 %}s{% endif %}</p> </form>
</div> {% else %}
{% endfor %} {% with templates=templates, template_folders=template_folders %}
{% for template in templates %} {% include 'views/templates/_template_list.html' %}
<div class="column-whole"> {% endwith %}
<h2 class="message-name"> {% endif %}
<a href="{{ url_for('.view_template', service_id=current_service.id, template_id=template.id) }}">{{ template.name }}</a>
</h2>
<p class="message-type">
{{ message_count_label(1, template.template_type, suffix='')|capitalize }} template
</p>
</div>
{% endfor %}
</nav>
{% endif %} {% endif %}
{% endblock %} {% endblock %}
+54 -1
View File
@@ -22,6 +22,7 @@ from tests.conftest import (
TEMPLATE_ONE_ID, TEMPLATE_ONE_ID,
active_caseworking_user, active_caseworking_user,
active_user_view_permissions, active_user_view_permissions,
active_user_with_permissions,
mock_get_service_email_template, mock_get_service_email_template,
mock_get_service_letter_template, mock_get_service_letter_template,
mock_get_service_template, mock_get_service_template,
@@ -135,6 +136,58 @@ def test_should_show_page_for_choosing_a_template(
mock_get_service_templates.assert_called_once_with(SERVICE_ONE_ID) mock_get_service_templates.assert_called_once_with(SERVICE_ONE_ID)
@pytest.mark.parametrize('user', [
pytest.param(
active_user_with_permissions
),
pytest.param(
active_user_view_permissions,
marks=pytest.mark.xfail(raises=AssertionError)
),
pytest.param(
active_caseworking_user,
marks=pytest.mark.xfail(raises=AssertionError)
),
])
@pytest.mark.parametrize('extra_service_permissions', [
pytest.param(
['edit_folders']
),
pytest.param(
[],
marks=pytest.mark.xfail(raises=AssertionError)
),
])
def test_should_show_checkboxes_for_selecting_templates(
client_request,
mocker,
service_one,
mock_get_service_templates,
mock_get_template_folders,
mock_has_no_jobs,
fake_uuid,
user,
extra_service_permissions,
):
mocker.patch('app.user_api_client.get_user', return_value=user(fake_uuid))
service_one['permissions'] = service_one['permissions'] + extra_service_permissions
page = client_request.get(
'main.choose_template',
service_id=SERVICE_ONE_ID,
)
checkboxes = page.select('input[name=templates_and_folders]')
assert len(checkboxes) == 4
assert checkboxes[0]['value'] == TEMPLATE_ONE_ID
assert checkboxes[0]['id'] == 'templates-or-folder-{}'.format(TEMPLATE_ONE_ID)
for index in (1, 2, 3):
assert checkboxes[index]['value'] != TEMPLATE_ONE_ID
assert TEMPLATE_ONE_ID not in checkboxes[index]['id']
def test_should_not_show_template_nav_if_only_one_type_of_template( def test_should_not_show_template_nav_if_only_one_type_of_template(
client_request, client_request,
mock_get_template_folders, mock_get_template_folders,
@@ -176,7 +229,7 @@ def test_should_show_live_search_if_list_of_templates_taller_than_screen(
search = page.select_one('.live-search') search = page.select_one('.live-search')
assert search['data-module'] == 'live-search' assert search['data-module'] == 'live-search'
assert search['data-targets'] == '#template-list .column-whole' assert search['data-targets'] == '#template-list .template-list-item'
assert len(page.select(search['data-targets'])) == len(page.select('.message-name')) == 14 assert len(page.select(search['data-targets'])) == len(page.select('.message-name')) == 14