mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-08-18 21:49:37 -04:00
Merge pull request #2559 from alphagov/template-js
Template folder form smart JS
This commit is contained in:
114
app/assets/javascripts/templateFolderForm.js
Normal file
114
app/assets/javascripts/templateFolderForm.js
Normal 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);
|
||||
@@ -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')
|
||||
])
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -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() }}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
Reference in New Issue
Block a user