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 */ /* 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 = { var Api = {
groupsPath: "/api/:version/groups.json", groupsPath: '/api/:version/groups.json',
groupPath: "/api/:version/groups/:id.json", groupPath: '/api/:version/groups/:id.json',
namespacesPath: "/api/:version/namespaces.json", namespacesPath: '/api/:version/namespaces.json',
groupProjectsPath: "/api/:version/groups/:id/projects.json", groupProjectsPath: '/api/:version/groups/:id/projects.json',
projectsPath: "/api/:version/projects.json?simple=true", projectsPath: '/api/:version/projects.json?simple=true',
labelsPath: "/:namespace_path/:project_path/labels", labelsPath: '/:namespace_path/:project_path/labels',
licensePath: "/api/:version/templates/licenses/:key", licensePath: '/api/:version/templates/licenses/:key',
gitignorePath: "/api/:version/templates/gitignores/:key", gitignorePath: '/api/:version/templates/gitignores/:key',
gitlabCiYmlPath: "/api/:version/templates/gitlab_ci_ymls/:key", gitlabCiYmlPath: '/api/:version/templates/gitlab_ci_ymls/:key',
ldapGroupsPath: "/api/:version/ldap/:provider/groups.json", ldapGroupsPath: '/api/:version/ldap/:provider/groups.json',
dockerfilePath: "/api/:version/templates/dockerfiles/:key", dockerfilePath: '/api/:version/templates/dockerfiles/:key',
issuableTemplatePath: "/:namespace_path/:project_path/templates/:type/:key", issuableTemplatePath: '/:namespace_path/:project_path/templates/:type/:key',
group: function(group_id, callback) { group: function(group_id, callback) {
var url = Api.buildUrl(Api.groupPath) var url = Api.buildUrl(Api.groupPath)
.replace(':id', group_id); .replace(':id', group_id);
return $.ajax({ return $.ajax({
url: url, url: url,
dataType: "json" dataType: 'json'
}).done(function(group) { }).done(function(group) {
return callback(group); return callback(group);
}); });
}, },
users: function(search, options, callback = $.noop) { users: function(search, options, callback = $.noop) {
var url = Api.buildUrl("/autocomplete/users.json"); var url = Api.buildUrl('/autocomplete/users.json');
return $.ajax({ return $.ajax({
url, url,
data: $.extend({ data: $.extend({
...@@ -43,7 +43,7 @@ var Api = { ...@@ -43,7 +43,7 @@ var Api = {
search: query, search: query,
per_page: 20 per_page: 20
}, options), }, options),
dataType: "json" dataType: 'json'
}).done(function(groups) { }).done(function(groups) {
return callback(groups); return callback(groups);
}); });
...@@ -57,7 +57,7 @@ var Api = { ...@@ -57,7 +57,7 @@ var Api = {
search: query, search: query,
per_page: 20 per_page: 20
}, },
dataType: "json" dataType: 'json'
}).done(function(namespaces) { }).done(function(namespaces) {
return callback(namespaces); return callback(namespaces);
}); });
...@@ -72,7 +72,7 @@ var Api = { ...@@ -72,7 +72,7 @@ var Api = {
per_page: 20, per_page: 20,
membership: true membership: true
}, options), }, options),
dataType: "json" dataType: 'json'
}).done(function(projects) { }).done(function(projects) {
return callback(projects); return callback(projects);
}); });
...@@ -83,9 +83,9 @@ var Api = { ...@@ -83,9 +83,9 @@ var Api = {
.replace(':project_path', project_path); .replace(':project_path', project_path);
return $.ajax({ return $.ajax({
url: url, url: url,
type: "POST", type: 'POST',
data: { 'label': data }, data: { 'label': data },
dataType: "json" dataType: 'json'
}).done(function(label) { }).done(function(label) {
return callback(label); return callback(label);
}).error(function(message) { }).error(function(message) {
...@@ -102,7 +102,7 @@ var Api = { ...@@ -102,7 +102,7 @@ var Api = {
search: query, search: query,
per_page: 20 per_page: 20
}, },
dataType: "json" dataType: 'json'
}).done(function(projects) { }).done(function(projects) {
return callback(projects); return callback(projects);
}); });
...@@ -167,7 +167,7 @@ var Api = { ...@@ -167,7 +167,7 @@ var Api = {
per_page: 20, per_page: 20,
active: true active: true
}, },
dataType: "json" dataType: 'json'
}).done(function(groups) { }).done(function(groups) {
return callback(groups); return callback(groups);
}); });
......
...@@ -2,88 +2,54 @@ ...@@ -2,88 +2,54 @@
export default class ApproversSelect { export default class ApproversSelect {
constructor() { constructor() {
const approverSelect = document.querySelector('.js-select-user-and-group'); this.$approverSelect = $('.js-select-user-and-group');
const name = approverSelect.dataset.name; const name = this.$approverSelect.data('name');
this.fieldNames = [`${name}[approver_ids]`, `${name}[approver_group_ids]`]; this.fieldNames = [`${name}[approver_ids]`, `${name}[approver_group_ids]`];
this.$loadWrapper = $('.load-wrapper');
this.bindEvents(); this.bindEvents();
this.addEvents(); this.addEvents();
this.initSelect2();
$('.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);
} }
bindEvents() { bindEvents() {
this.addApprover = this.addApprover.bind(this);
this.handleSelectChange = this.handleSelectChange.bind(this); this.handleSelectChange = this.handleSelectChange.bind(this);
this.fetchGroups = this.fetchGroups.bind(this); this.fetchGroups = this.fetchGroups.bind(this);
this.fetchUsers = this.fetchUsers.bind(this); this.fetchUsers = this.fetchUsers.bind(this);
} }
addEvents() { addEvents() {
$(document).on('click', '.js-approvers', this.addApprover); $(document).on('click', '.js-add-approvers', () => this.addApprover());
$(document).on('click', '.js-approver-remove', ApproversSelect.removeApprover); $(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 input = $(`[name="${fieldName}"]`);
const existingApprovers = [].map.call(
document.querySelectorAll(selector), const existingApprovers = $(selector).map((i, el) =>
item => parseInt(item.getAttribute('data-id'), 10), parseInt($(el).data('id'), 10),
); );
const selectedApprovers = input.val() const selectedApprovers = input.val()
.split(',') .split(',')
.filter(val => val !== ''); .filter(val => val !== '');
const options = { const approvers = {
[key]: [...existingApprovers, ...selectedApprovers], [key]: [...existingApprovers, ...selectedApprovers],
}; };
return options; return approvers;
} }
fetchGroups(term) { 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); return Api.groups(term, options);
} }
fetchUsers(term) { 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); return Api.users(term, options);
} }
handleSelectChange(evt) { handleSelectChange(e) {
const { added, removed } = evt; const { added, removed } = e;
const userInput = $(`[name="${this.fieldNames[0]}"]`); const userInput = $(`[name="${this.fieldNames[0]}"]`);
const groupInput = $(`[name="${this.fieldNames[1]}"]`); const groupInput = $(`[name="${this.fieldNames[1]}"]`);
...@@ -104,16 +70,53 @@ export default class ApproversSelect { ...@@ -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) { static formatSelection(group) {
return group.full_name || group.name; return group.full_name || group.name;
} }
static formatResult({ static formatResult({
name,
username,
avatar_url: avatarUrl, avatar_url: avatarUrl,
full_name: fullName, full_name: fullName,
full_path: fullPath, full_path: fullPath,
name,
username,
}) { }) {
if (username) { if (username) {
const avatar = avatarUrl || gon.default_avatar_url; const avatar = avatarUrl || gon.default_avatar_url;
...@@ -145,13 +148,15 @@ export default class ApproversSelect { ...@@ -145,13 +148,15 @@ export default class ApproversSelect {
static saveApprovers(fieldName) { static saveApprovers(fieldName) {
const $input = $(`[name="${fieldName}"]`); const $input = $(`[name="${fieldName}"]`);
const newValue = $input.val(); const newValue = $input.val();
const $loadWrapper = $('.load-wrapper');
const $approverSelect = $('.js-select-user-and-group');
if (!newValue) { if (!newValue) {
return; return;
} }
const $form = $('.js-approvers').closest('form'); const $form = $('.js-add-approvers').closest('form');
$('.load-wrapper').removeClass('hidden'); $loadWrapper.removeClass('hidden');
$.ajax({ $.ajax({
url: $form.attr('action'), url: $form.attr('action'),
type: 'POST', type: 'POST',
...@@ -162,20 +167,20 @@ export default class ApproversSelect { ...@@ -162,20 +167,20 @@ export default class ApproversSelect {
success: ApproversSelect.updateApproverList, success: ApproversSelect.updateApproverList,
complete() { complete() {
$input.val('val', ''); $input.val('val', '');
$('.js-select-user-and-group').select2('val', ''); $approverSelect.select2('val', '');
$('.load-wrapper').addClass('hidden'); $loadWrapper.addClass('hidden');
}, },
error() { error() {
// TODO: scroll into view or toast
window.Flash('Failed to add Approver', 'alert'); window.Flash('Failed to add Approver', 'alert');
}, },
}); });
} }
static removeApprover(evt) { static removeApprover(e) {
evt.preventDefault(); e.preventDefault();
const target = evt.currentTarget; const target = e.currentTarget;
$('.load-wrapper').removeClass('hidden'); const $loadWrapper = $('.load-wrapper');
$loadWrapper.removeClass('hidden');
$.ajax({ $.ajax({
url: target.getAttribute('href'), url: target.getAttribute('href'),
type: 'POST', type: 'POST',
...@@ -183,7 +188,7 @@ export default class ApproversSelect { ...@@ -183,7 +188,7 @@ export default class ApproversSelect {
_method: 'DELETE', _method: 'DELETE',
}, },
success: ApproversSelect.updateApproverList, success: ApproversSelect.updateApproverList,
complete: () => $('.load-wrapper').addClass('hidden'), complete: () => $loadWrapper.addClass('hidden'),
error() { error() {
window.Flash('Failed to remove Approver', 'alert'); window.Flash('Failed to remove Approver', 'alert');
}, },
......
...@@ -10,7 +10,7 @@ Vue.use(PDFLab, { ...@@ -10,7 +10,7 @@ Vue.use(PDFLab, {
export default () => { export default () => {
const el = document.getElementById('js-pdf-viewer'); const el = document.getElementById('js-pdf-viewer');
return new Vue({ new Vue({
el, el,
data() { data() {
return { return {
......
...@@ -22,11 +22,8 @@ ...@@ -22,11 +22,8 @@
} }
ProjectNew.prototype.bindEvents = function() { ProjectNew.prototype.bindEvents = function() {
this.toggleSettings = this.toggleSettings.bind(this); this.$selects.on('change', () => this.toggleSettings());
this.toggleApproverSettingsVisibility = this.toggleApproverSettingsVisibility.bind(this); $('#require_approvals').on('change', e => this.toggleApproverSettingsVisibility(e));
this.$selects.on('change', this.toggleSettings);
$('#require_approvals').on('change', this.toggleApproverSettingsVisibility);
}; };
ProjectNew.prototype.initVisibilitySelect = function() { ProjectNew.prototype.initVisibilitySelect = function() {
...@@ -36,9 +33,9 @@ ...@@ -36,9 +33,9 @@
visibilitySelect.init(); visibilitySelect.init();
}; };
ProjectNew.prototype.toggleApproverSettingsVisibility = function(evt) { ProjectNew.prototype.toggleApproverSettingsVisibility = function(e) {
this.$requiredApprovals = $('#project_approvals_before_merge'); this.$requiredApprovals = $('#project_approvals_before_merge');
const enabled = $(evt.target).prop('checked'); const enabled = $(e.target).prop('checked');
const val = enabled ? 1 : 0; const val = enabled ? 1 : 0;
this.$requiredApprovals.val(val); this.$requiredApprovals.val(val);
this.$requiredApprovals.prop('min', val); this.$requiredApprovals.prop('min', val);
......
...@@ -46,20 +46,20 @@ ...@@ -46,20 +46,20 @@
.form-group.reset-approvals-on-push .form-group.reset-approvals-on-push
.checkbox .checkbox
= label_tag :require_approvals do = 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 %strong Activate merge request approvals
= link_to icon('question-circle'), help_page_path("user/project/merge_requests/merge_request_approvals"), target: '_blank' = 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. .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-group
= form.label :approver_ids, class: 'label-light' do = form.label :approver_ids, class: 'label-light' do
Approvers Approvers
= hidden_field_tag "project[approver_ids]" = hidden_field_tag "project[approver_ids]"
= hidden_field_tag "project[approver_group_ids]" = hidden_field_tag "project[approver_group_ids]"
.input-group.input-btn-group .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' }} = 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)' } %button.btn.btn-success.js-add-approvers{ type: 'button', title: 'Add approvers(s)' }
Add Add
.help-block .help-block
Add an approver or group suggestion for each merge request Add an approver or group suggestion for each merge request
......
...@@ -3,17 +3,6 @@ import testPDF from './test.pdf'; ...@@ -3,17 +3,6 @@ import testPDF from './test.pdf';
describe('PDF renderer', () => { describe('PDF renderer', () => {
let viewer; let viewer;
let app;
const checkLoaded = (done) => {
if (app.loading) {
setTimeout(() => {
checkLoaded(done);
}, 100);
} else {
done();
}
};
preloadFixtures('static/pdf_viewer.html.raw'); preloadFixtures('static/pdf_viewer.html.raw');
...@@ -33,9 +22,11 @@ describe('PDF renderer', () => { ...@@ -33,9 +22,11 @@ describe('PDF renderer', () => {
describe('successful response', () => { describe('successful response', () => {
beforeEach((done) => { beforeEach((done) => {
app = renderPDF(); renderPDF();
checkLoaded(done); setTimeout(() => {
done();
}, 500);
}); });
it('does not show loading icon', () => { it('does not show loading icon', () => {
...@@ -59,10 +50,11 @@ describe('PDF renderer', () => { ...@@ -59,10 +50,11 @@ describe('PDF renderer', () => {
describe('error getting file', () => { describe('error getting file', () => {
beforeEach((done) => { beforeEach((done) => {
viewer.dataset.endpoint = 'invalid/endpoint'; renderPDF();
app = renderPDF();
checkLoaded(done); setTimeout(() => {
done();
}, 500);
}); });
it('does not show loading icon', () => { 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