Merge pull request #2559 from alphagov/template-js

Template folder form smart JS
This commit is contained in:
Chris Hill-Scott
2018-12-05 16:36:12 +00:00
committed by GitHub
10 changed files with 172 additions and 43 deletions

View File

@@ -0,0 +1,114 @@
(function(Modules) {
"use strict";
Modules.TemplateFolderForm = function() {
this.start = function(templateFolderForm) {
this.$form = $(templateFolderForm);
// remove the hidden unknown button - if you've got JS enabled then the action you want to do is implied by
// which field is visible.
this.$form.find('button[value=unknown]').remove();
this.$stickyBottom = this.$form.find('#sticky_template_forms');
this.$stickyBottom.append(this.nothingSelectedButtons);
this.$stickyBottom.append(this.itemsSelectedButtons);
// all the diff states that we want to show or hide
this.states = [
{key: 'nothing-selected-buttons', $el: this.$form.find('#nothing_selected'), cancellable: false},
{key: 'items-selected-buttons', $el: this.$form.find('#items_selected'), cancellable: false},
{key: 'move-to-existing-folder', $el: this.$form.find('#move_to_folder_radios'), cancellable: true},
{key: 'move-to-new-folder', $el: this.$form.find('#move_to_new_folder_form'), cancellable: true},
{key: 'add-new-folder', $el: this.$form.find('#add_new_folder_form'), cancellable: true},
{key: 'add-new-template', $el: this.$form.find('#add_new_template_form'), cancellable: true}
];
// cancel buttons only relevant if JS enabled, so
this.states.filter(state => state.cancellable).forEach((x) => this.addCancelButton(x));
// first off show the new template / new folder buttons
this.currentState = this.$form.data('prev-state') || 'unknown';
if (this.currentState === 'unknown') {
this.selectActionButtons();
}
this.$form.on('click', 'button.button-secondary', (event) => this.actionButtonClicked(event));
this.$form.on('change', 'input[type=checkbox]', () => this.templateFolderCheckboxChanged());
this.render();
};
this.addCancelButton = function(state) {
let $cancel = $('<a></a>')
.html('Cancel')
.click((event) => {
event.preventDefault();
// clear existing data
state.$el.find('input:radio').prop('checked', false);
state.$el.find('input:text').val('');
// go back to action buttons
this.selectActionButtons();
});
state.$el.append($cancel);
};
this.selectActionButtons = function () {
// If we want to show one of the grey choose actions state, we can pretend we're in the choose actions state,
// and then pretend a checkbox was clicked to work out whether to show zero or non-zero options.
// This calls a render at the end
this.currentState = 'nothing-selected-buttons';
this.templateFolderCheckboxChanged();
};
this.actionButtonClicked = function(event) {
event.preventDefault();
this.currentState = $(event.currentTarget).val();
this.render();
};
this.templateFolderCheckboxChanged = function() {
let numSelected = this.countSelectedCheckboxes();
if (this.currentState === 'nothing-selected-buttons' && numSelected !== 0) {
// user has just selected first item
this.currentState = 'items-selected-buttons';
} else if (this.currentState === 'items-selected-buttons' && numSelected === 0) {
// user has just deselected last item
this.currentState = 'nothing-selected-buttons';
}
this.render();
};
this.countSelectedCheckboxes = function() {
return this.$form.find('input:checkbox:checked').length;
};
this.render = function() {
// detach everything, unless they are the currentState
this.states.forEach(
state => (state.key === this.currentState ? this.$stickyBottom.append(state.$el) : state.$el.detach())
);
};
this.nothingSelectedButtons = `
<div id="nothing_selected">
<button class="button-secondary" value="add-new-template">New template</button>
<button class="button-secondary" value="add-new-folder">New folder</button>
</div>
`;
this.itemsSelectedButtons = `
<div id="items_selected">
<button class="button-secondary" value="move-to-existing-folder">Move</button>
<button class="button-secondary" value="move-to-new-folder">Add to a new folder</button>
</div>
`;
};
})(window.GOVUK.Modules);

View File

@@ -1165,11 +1165,11 @@ class TemplateAndFoldersSelectionForm(Form):
* unknown
currently not implemented, but in the future will try and work out if there are any obvious commands that can be
assumed based on which fields are empty vs populated.
* move_to_existing_folder
* move-to-existing-folder
must have data for templates_and_folders checkboxes, and move_to radios
* move_to_new_folder
* move-to-new-folder
must have data for move_to_new_folder_name, cannot have data for move_to_existing_folder_name
* add_new_folder
* add-new-folder
must have data for move_to_existing_folder_name, cannot have data for move_to_new_folder_name
"""
@@ -1212,12 +1212,15 @@ class TemplateAndFoldersSelectionForm(Form):
('copy-existing', 'Copy of an existing template') if allow_adding_copy_of_template else None,
]))
def is_selected(self, template_folder_id):
return template_folder_id in (self.templates_and_folders.data or [])
def validate(self):
self.op = request.form.get('operation')
self.is_move_op = self.op in {'move_to_existing_folder', 'move_to_new_folder'}
self.is_add_folder_op = self.op in {'add_new_folder', 'move_to_new_folder'}
self.is_add_template_op = self.op in {'add_template'}
self.is_move_op = self.op in {'move-to-existing-folder', 'move-to-new-folder'}
self.is_add_folder_op = self.op in {'add-new-folder', 'move-to-new-folder'}
self.is_add_template_op = self.op in {'add-new-template'}
if not (self.is_add_folder_op or self.is_move_op or self.is_add_template_op):
return False
@@ -1225,23 +1228,23 @@ class TemplateAndFoldersSelectionForm(Form):
return super().validate()
def get_folder_name(self):
if self.op == 'add_new_folder':
if self.op == 'add-new-folder':
return self.add_new_folder_name.data
elif self.op == 'move_to_new_folder':
elif self.op == 'move-to-new-folder':
return self.move_to_new_folder_name.data
return None
templates_and_folders = MultiCheckboxField('Choose templates or folders', validators=[
required_for_ops('move_to_new_folder', 'move_to_existing_folder')
required_for_ops('move-to-new-folder', 'move-to-existing-folder')
])
move_to = RadioFieldWithNoneOption('Choose a folder', validators=[
Optional(),
required_for_ops('move_to_new_folder', 'move_to_existing_folder')
required_for_ops('move-to-new-folder', 'move-to-existing-folder')
])
add_new_folder_name = StringField('Folder name', validators=[required_for_ops('add_new_folder')])
move_to_new_folder_name = StringField('Folder name', validators=[required_for_ops('move_to_new_folder')])
add_new_folder_name = StringField('Folder name', validators=[required_for_ops('add-new-folder')])
move_to_new_folder_name = StringField('Folder name', validators=[required_for_ops('move-to-new-folder')])
add_template_by_template_type = RadioField('Add new', validators=[
Optional(),
required_for_ops('add_template')
required_for_ops('add-new-template')
])

View File

@@ -122,7 +122,6 @@ def choose_template(service_id, template_type='all', template_folder_id=None):
len(user_api_client.get_service_ids_for_user(current_user)) > 1
),
)
if request.method == 'POST' and templates_and_folders_form.validate_on_submit():
if not can_manage_folders():
abort(403)
@@ -142,7 +141,7 @@ def choose_template(service_id, template_type='all', template_folder_id=None):
template_nav_items=get_template_nav_items(template_folder_id),
template_type=template_type,
search_form=SearchTemplatesForm(),
templates_and_folders_form=templates_and_folders_form
templates_and_folders_form=templates_and_folders_form,
)

View File

@@ -4,7 +4,8 @@
autocomplete=False,
class=None,
id=None,
module=None
module=None,
data_kwargs={}
) %}
<form
method="{{ method }}"
@@ -13,6 +14,11 @@
{% if class %}class="{{ class }}"{% endif %}
{% if id %}id="{{ id }}"{% endif %}
{% if module %}data-module="{{ module }}"{% endif %}
{% for key, val in data_kwargs.items() %}
{% if val %}
data-{{ key }}="{{ val }}"
{% endif %}
{% endfor %}
novalidate
>
{{ caller() }}

View File

@@ -1,17 +1,19 @@
{% from "components/radios.html" import radios %}
{% from "components/page-footer.html" import page_footer %}
<div id="sticky_template_forms" class="js-stick-at-bottom-when-scrolling">
<button type="submit" name="operation" value="unknown" hidden></button>
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
{% if templates_and_folders_form.move_to.choices and template_list.templates_to_show %}
<div id="move_to_folder_radios">
{{ radios(templates_and_folders_form.move_to) }}
{{ page_footer('Move', button_name='operation', button_value='move_to_existing_folder') }}
{{ page_footer('Move', button_name='operation', button_value='move-to-existing-folder') }}
</div>
<div id="move_to_new_folder_form">
<fieldset>
<legend class="visuallyhidden">Move to a new folder</legend>
{{ textbox(templates_and_folders_form.move_to_new_folder_name) }}
{{ page_footer('Move to a new folder', button_name='operation', button_value='move_to_new_folder') }}
{{ page_footer('Move to a new folder', button_name='operation', button_value='move-to-new-folder') }}
</fieldset>
</div>
{% endif %}
@@ -19,13 +21,14 @@
<fieldset>
<legend class="visuallyhidden">Add a new folder</legend>
{{ textbox(templates_and_folders_form.add_new_folder_name) }}
{{ page_footer('New folder', button_name='operation', button_value='add_new_folder') }}
{{ page_footer('New folder', button_name='operation', button_value='add-new-folder') }}
</fieldset>
</div>
<div id="add_new_template_form">
<fieldset>
<legend class="visuallyhidden">Add a new template</legend>
{{ radios(templates_and_folders_form.add_template_by_template_type) }}
{{ page_footer('Continue', button_name='operation', button_value='add_template') }}
{{ page_footer('Continue', button_name='operation', button_value='add-new-template') }}
</fieldset>
</div>
</div>

View File

@@ -17,6 +17,7 @@
{{ unlabelled_checkbox(
id='templates-or-folder-{}'.format(item.id),
name='templates_and_folders',
data=templates_and_folders_form.is_selected(item.id),
value=item.id,
) }}
{% endif %}

View File

@@ -60,8 +60,9 @@
</div>
{% if current_user.has_permissions('manage_templates') %}
<div class="column-one-third">
{% if not can_manage_folders %}
<a href="{{ url_for('.add_template_by_type', service_id=current_service.id, template_folder_id=current_template_folder_id) }}" class="button align-with-heading">Add new template</a>
{% endif %}
{% if can_manage_folders and current_template_folder_id %}
<a href="{{ url_for('.manage_template_folder', service_id=current_service.id, template_folder_id=current_template_folder_id) }}" class="align-with-heading">Manage</a>
{% endif %}
@@ -78,11 +79,12 @@
{{ live_search(target_selector='#template-list .template-list-item', show=show_search_box, form=search_form) }}
{% if can_manage_folders %}
{% call form_wrapper() %}
{% call form_wrapper(
module='template-folder-form',
data_kwargs={'prev-state': templates_and_folders_form.op or None}
) %}
{% include 'views/templates/_template_list.html' %}
{% with templates=templates, template_folders=template_folders, templates_and_folders_form=templates_and_folders_form %}
{% include 'views/templates/_move_to.html' %}
{% endwith %}
{% include 'views/templates/_move_to.html' %}
{% endcall %}
{% else %}