Commit fd1908a5 authored by Jose Ivan Vargas's avatar Jose Ivan Vargas

Improvements to event listeners and cached selectors

parent 1beaad2f
/* eslint-disable func-names, space-before-function-paren, quotes, object-shorthand, camelcase, no-var, comma-dangle, prefer-arrow-callback, quote-props, no-param-reassign, max-len */
var Api = {
groupsPath: "/api/:version/groups.json",
groupPath: "/api/:version/groups/:id.json",
namespacesPath: "/api/:version/namespaces.json",
groupProjectsPath: "/api/:version/groups/:id/projects.json",
projectsPath: "/api/:version/projects.json?simple=true",
labelsPath: "/:namespace_path/:project_path/labels",
licensePath: "/api/:version/templates/licenses/:key",
gitignorePath: "/api/:version/templates/gitignores/:key",
gitlabCiYmlPath: "/api/:version/templates/gitlab_ci_ymls/:key",
ldapGroupsPath: "/api/:version/ldap/:provider/groups.json",
dockerfilePath: "/api/:version/templates/dockerfiles/:key",
issuableTemplatePath: "/:namespace_path/:project_path/templates/:type/:key",
groupsPath: '/api/:version/groups.json',
groupPath: '/api/:version/groups/:id.json',
namespacesPath: '/api/:version/namespaces.json',
groupProjectsPath: '/api/:version/groups/:id/projects.json',
projectsPath: '/api/:version/projects.json?simple=true',
labelsPath: '/:namespace_path/:project_path/labels',
licensePath: '/api/:version/templates/licenses/:key',
gitignorePath: '/api/:version/templates/gitignores/:key',
gitlabCiYmlPath: '/api/:version/templates/gitlab_ci_ymls/:key',
ldapGroupsPath: '/api/:version/ldap/:provider/groups.json',
dockerfilePath: '/api/:version/templates/dockerfiles/:key',
issuableTemplatePath: '/:namespace_path/:project_path/templates/:type/:key',
group: function(group_id, callback) {
var url = Api.buildUrl(Api.groupPath)
.replace(':id', group_id);
return $.ajax({
url: url,
dataType: "json"
dataType: 'json'
}).done(function(group) {
return callback(group);
});
},
users: function(search, options, callback = $.noop) {
var url = Api.buildUrl("/autocomplete/users.json");
var url = Api.buildUrl('/autocomplete/users.json');
return $.ajax({
url,
data: $.extend({
......@@ -43,7 +43,7 @@ var Api = {
search: query,
per_page: 20
}, options),
dataType: "json"
dataType: 'json'
}).done(function(groups) {
return callback(groups);
});
......@@ -57,7 +57,7 @@ var Api = {
search: query,
per_page: 20
},
dataType: "json"
dataType: 'json'
}).done(function(namespaces) {
return callback(namespaces);
});
......@@ -72,7 +72,7 @@ var Api = {
per_page: 20,
membership: true
}, options),
dataType: "json"
dataType: 'json'
}).done(function(projects) {
return callback(projects);
});
......@@ -83,9 +83,9 @@ var Api = {
.replace(':project_path', project_path);
return $.ajax({
url: url,
type: "POST",
type: 'POST',
data: { 'label': data },
dataType: "json"
dataType: 'json'
}).done(function(label) {
return callback(label);
}).error(function(message) {
......@@ -102,7 +102,7 @@ var Api = {
search: query,
per_page: 20
},
dataType: "json"
dataType: 'json'
}).done(function(projects) {
return callback(projects);
});
......@@ -167,7 +167,7 @@ var Api = {
per_page: 20,
active: true
},
dataType: "json"
dataType: 'json'
}).done(function(groups) {
return callback(groups);
});
......
......@@ -2,88 +2,54 @@
export default class ApproversSelect {
constructor() {
const approverSelect = document.querySelector('.js-select-user-and-group');
const name = approverSelect.dataset.name;
this.$approverSelect = $('.js-select-user-and-group');
const name = this.$approverSelect.data('name');
this.fieldNames = [`${name}[approver_ids]`, `${name}[approver_group_ids]`];
this.$loadWrapper = $('.load-wrapper');
this.bindEvents();
this.addEvents();
$('.js-select-user-and-group').select2({
placeholder: 'Search for users or groups',
multiple: true,
minimumInputLength: 0,
query: (query) => {
const fetchGroups = this.fetchGroups(query.term);
const fetchUsers = this.fetchUsers(query.term);
return $.when(fetchGroups, fetchUsers).then((groups, users) => {
const data = {
results: groups[0].concat(users[0]),
};
return query.callback(data);
});
},
formatResult: ApproversSelect.formatResult,
formatSelection: ApproversSelect.formatSelection,
dropdownCss() {
const $input = $('.js-select-user-and-group .select2-input');
const offset = $input.offset();
const inputRight = offset.left + $input.outerWidth();
const $dropdown = $('.select2-drop-active');
let left = offset.left;
if ($dropdown.outerWidth() > $input.outerWidth()) {
left = `${inputRight - $dropdown.width()}px`;
}
return {
left,
right: 'auto',
width: 'auto',
};
},
})
.on('change', this.handleSelectChange);
this.initSelect2();
}
bindEvents() {
this.addApprover = this.addApprover.bind(this);
this.handleSelectChange = this.handleSelectChange.bind(this);
this.fetchGroups = this.fetchGroups.bind(this);
this.fetchUsers = this.fetchUsers.bind(this);
}
addEvents() {
$(document).on('click', '.js-approvers', this.addApprover);
$(document).on('click', '.js-approver-remove', ApproversSelect.removeApprover);
$(document).on('click', '.js-add-approvers', () => this.addApprover());
$(document).on('click', '.js-approver-remove', e => ApproversSelect.removeApprover(e));
}
static getOptions(fieldName, selector, key) {
static getApprovers(fieldName, selector, key) {
const input = $(`[name="${fieldName}"]`);
const existingApprovers = [].map.call(
document.querySelectorAll(selector),
item => parseInt(item.getAttribute('data-id'), 10),
const existingApprovers = $(selector).map((i, el) =>
parseInt($(el).data('id'), 10),
);
const selectedApprovers = input.val()
.split(',')
.filter(val => val !== '');
const options = {
const approvers = {
[key]: [...existingApprovers, ...selectedApprovers],
};
return options;
return approvers;
}
fetchGroups(term) {
const options = ApproversSelect.getOptions(this.fieldNames[1], '.js-approver-group', 'skip_groups');
const options = ApproversSelect.getApprovers(this.fieldNames[1], '.js-approver-group', 'skip_groups');
return Api.groups(term, options);
}
fetchUsers(term) {
const options = ApproversSelect.getOptions(this.fieldNames[0], '.js-approver', 'skip_users');
const options = ApproversSelect.getApprovers(this.fieldNames[0], '.js-approver', 'skip_users');
return Api.users(term, options);
}
handleSelectChange(evt) {
const { added, removed } = evt;
handleSelectChange(e) {
const { added, removed } = e;
const userInput = $(`[name="${this.fieldNames[0]}"]`);
const groupInput = $(`[name="${this.fieldNames[1]}"]`);
......@@ -104,16 +70,53 @@ export default class ApproversSelect {
}
}
initSelect2() {
this.$approverSelect.select2({
placeholder: 'Search for users or groups',
multiple: true,
minimumInputLength: 0,
query: (query) => {
const fetchGroups = this.fetchGroups(query.term);
const fetchUsers = this.fetchUsers(query.term);
return $.when(fetchGroups, fetchUsers).then((groups, users) => {
const data = {
results: groups[0].concat(users[0]),
};
return query.callback(data);
});
},
formatResult: ApproversSelect.formatResult,
formatSelection: ApproversSelect.formatSelection,
dropdownCss() {
const $input = $('.js-select-user-and-group .select2-input');
const offset = $input.offset();
const inputRightPosition = offset.left + $input.outerWidth();
const $dropdown = $('.select2-drop-active');
let left = offset.left;
if ($dropdown.outerWidth() > $input.outerWidth()) {
left = `${inputRightPosition - $dropdown.width()}px`;
}
return {
left,
right: 'auto',
width: 'auto',
};
},
})
.on('change', this.handleSelectChange);
}
static formatSelection(group) {
return group.full_name || group.name;
}
static formatResult({
name,
username,
avatar_url: avatarUrl,
full_name: fullName,
full_path: fullPath,
name,
username,
}) {
if (username) {
const avatar = avatarUrl || gon.default_avatar_url;
......@@ -145,13 +148,15 @@ export default class ApproversSelect {
static saveApprovers(fieldName) {
const $input = $(`[name="${fieldName}"]`);
const newValue = $input.val();
const $loadWrapper = $('.load-wrapper');
const $approverSelect = $('.js-select-user-and-group');
if (!newValue) {
return;
}
const $form = $('.js-approvers').closest('form');
$('.load-wrapper').removeClass('hidden');
const $form = $('.js-add-approvers').closest('form');
$loadWrapper.removeClass('hidden');
$.ajax({
url: $form.attr('action'),
type: 'POST',
......@@ -162,20 +167,20 @@ export default class ApproversSelect {
success: ApproversSelect.updateApproverList,
complete() {
$input.val('val', '');
$('.js-select-user-and-group').select2('val', '');
$('.load-wrapper').addClass('hidden');
$approverSelect.select2('val', '');
$loadWrapper.addClass('hidden');
},
error() {
// TODO: scroll into view or toast
window.Flash('Failed to add Approver', 'alert');
},
});
}
static removeApprover(evt) {
evt.preventDefault();
const target = evt.currentTarget;
$('.load-wrapper').removeClass('hidden');
static removeApprover(e) {
e.preventDefault();
const target = e.currentTarget;
const $loadWrapper = $('.load-wrapper');
$loadWrapper.removeClass('hidden');
$.ajax({
url: target.getAttribute('href'),
type: 'POST',
......@@ -183,7 +188,7 @@ export default class ApproversSelect {
_method: 'DELETE',
},
success: ApproversSelect.updateApproverList,
complete: () => $('.load-wrapper').addClass('hidden'),
complete: () => $loadWrapper.addClass('hidden'),
error() {
window.Flash('Failed to remove Approver', 'alert');
},
......
......@@ -10,7 +10,7 @@ Vue.use(PDFLab, {
export default () => {
const el = document.getElementById('js-pdf-viewer');
return new Vue({
new Vue({
el,
data() {
return {
......
......@@ -22,11 +22,8 @@
}
ProjectNew.prototype.bindEvents = function() {
this.toggleSettings = this.toggleSettings.bind(this);
this.toggleApproverSettingsVisibility = this.toggleApproverSettingsVisibility.bind(this);
this.$selects.on('change', this.toggleSettings);
$('#require_approvals').on('change', this.toggleApproverSettingsVisibility);
this.$selects.on('change', () => this.toggleSettings());
$('#require_approvals').on('change', e => this.toggleApproverSettingsVisibility(e));
};
ProjectNew.prototype.initVisibilitySelect = function() {
......@@ -36,9 +33,9 @@
visibilitySelect.init();
};
ProjectNew.prototype.toggleApproverSettingsVisibility = function(evt) {
ProjectNew.prototype.toggleApproverSettingsVisibility = function(e) {
this.$requiredApprovals = $('#project_approvals_before_merge');
const enabled = $(evt.target).prop('checked');
const enabled = $(e.target).prop('checked');
const val = enabled ? 1 : 0;
this.$requiredApprovals.val(val);
this.$requiredApprovals.prop('min', val);
......
......@@ -46,20 +46,20 @@
.form-group.reset-approvals-on-push
.checkbox
= label_tag :require_approvals do
= check_box_tag :require_approvals, nil, project.approvals_before_merge > 0, class: 'js-require-approvals-toggle'
= check_box_tag :require_approvals, nil, project.approvals_before_merge.nonzero?, class: 'js-require-approvals-toggle'
%strong Activate merge request approvals
= link_to icon('question-circle'), help_page_path("user/project/merge_requests/merge_request_approvals"), target: '_blank'
.descr Merge request approvals allow you to set the number of necessary approvals and predefine a list of approvers that you will need to approve every merge request in a project.
.nested-settings{ class: project.approvals_before_merge > 0 ? '' : 'hidden' }
.nested-settings{ class: project.approvals_before_merge.nonzero? ? '' : 'hidden' }
.form-group
= form.label :approver_ids, class: 'label-light' do
Approvers
= hidden_field_tag "project[approver_ids]"
= hidden_field_tag "project[approver_group_ids]"
.input-group.input-btn-group
= hidden_field_tag :approver_user_and_group_ids, '', { class: 'js-select-user-and-group input-large', tabindex: 1, data: { name: 'project' }}
%button.btn.btn-success.js-approvers{ type: 'button', title: 'Add approvers(s)' }
= hidden_field_tag :approver_user_and_group_ids, '', { class: 'js-select-user-and-group input-large', tabindex: 1, 'data-name': 'project' }
%button.btn.btn-success.js-add-approvers{ type: 'button', title: 'Add approvers(s)' }
Add
.help-block
Add an approver or group suggestion for each merge request
......
......@@ -3,17 +3,6 @@ import testPDF from './test.pdf';
describe('PDF renderer', () => {
let viewer;
let app;
const checkLoaded = (done) => {
if (app.loading) {
setTimeout(() => {
checkLoaded(done);
}, 100);
} else {
done();
}
};
preloadFixtures('static/pdf_viewer.html.raw');
......@@ -33,9 +22,11 @@ describe('PDF renderer', () => {
describe('successful response', () => {
beforeEach((done) => {
app = renderPDF();
renderPDF();
checkLoaded(done);
setTimeout(() => {
done();
}, 500);
});
it('does not show loading icon', () => {
......@@ -59,10 +50,11 @@ describe('PDF renderer', () => {
describe('error getting file', () => {
beforeEach((done) => {
viewer.dataset.endpoint = 'invalid/endpoint';
app = renderPDF();
renderPDF();
checkLoaded(done);
setTimeout(() => {
done();
}, 500);
});
it('does not show loading icon', () => {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment