summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke "Jared" Bennett <lbennett@gitlab.com>2017-03-08 11:59:39 +0000
committerLuke "Jared" Bennett <lbennett@gitlab.com>2017-03-15 09:42:18 +0000
commitbe06e01fd170624db2cf654dee9731bfd6bef13c (patch)
treea6f3da56e4f25098334ce52c8cbe1f57395906d2
parent9226ce9e3090dcedcb5c9fa8595d75b5eb295726 (diff)
downloadgitlab-ce-group-links-select2-infinite-scroll.tar.gz
Update select2 to include 3.5.x infinite scrollinggroup-links-select2-infinite-scroll
-rw-r--r--app/assets/javascripts/groups_select.js76
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js16
-rw-r--r--spec/features/projects/group_links_spec.rb22
-rw-r--r--spec/javascripts/lib/utils/common_utils_spec.js31
-rw-r--r--spec/support/select2_helper.rb8
5 files changed, 137 insertions, 16 deletions
diff --git a/app/assets/javascripts/groups_select.js b/app/assets/javascripts/groups_select.js
index e5dfa30edab..5b43c3fec44 100644
--- a/app/assets/javascripts/groups_select.js
+++ b/app/assets/javascripts/groups_select.js
@@ -6,23 +6,58 @@ var slice = [].slice;
window.GroupsSelect = (function() {
function GroupsSelect() {
$('.ajax-groups-select').each((function(_this) {
+ const self = _this;
+
return function(i, select) {
var all_available, skip_groups;
- all_available = $(select).data('all-available');
- skip_groups = $(select).data('skip-groups') || [];
- return $(select).select2({
+ const $select = $(select);
+ all_available = $select.data('all-available');
+ skip_groups = $select.data('skip-groups') || [];
+
+ $select.select2({
placeholder: "Search for a group",
- multiple: $(select).hasClass('multiselect'),
+ multiple: $select.hasClass('multiselect'),
minimumInputLength: 0,
- query: function(query) {
- var options = { all_available: all_available, skip_groups: skip_groups };
- return Api.groups(query.term, options, function(groups) {
- var data;
- data = {
- results: groups
+ ajax: {
+ url: Api.buildUrl(Api.groupsPath),
+ dataType: 'json',
+ quietMillis: 250,
+ transport: function (params) {
+ $.ajax(params).then((data, status, xhr) => {
+ const results = data || [];
+
+ const headers = gl.utils.normalizeCRLFHeaders(xhr.getAllResponseHeaders());
+ const currentPage = parseInt(headers['X-PAGE'], 10) || 0;
+ const totalPages = parseInt(headers['X-TOTAL-PAGES'], 10) || 0;
+ const more = currentPage < totalPages;
+
+ return {
+ results,
+ pagination: {
+ more,
+ },
+ };
+ }).then(params.success).fail(params.error);
+ },
+ data: function (search, page) {
+ return {
+ search,
+ page,
+ per_page: GroupsSelect.PER_PAGE,
+ };
+ },
+ results: function (data, page) {
+ if (data.length) return { results: [] };
+
+ const results = data.length ? data : data.results || [];
+ const more = data.pagination ? data.pagination.more : false;
+
+ return {
+ results,
+ page,
+ more,
};
- return query.callback(data);
- });
+ },
},
initSelection: function(element, callback) {
var id;
@@ -34,19 +69,23 @@ window.GroupsSelect = (function() {
formatResult: function() {
var args;
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
- return _this.formatResult.apply(_this, args);
+ return self.formatResult.apply(self, args);
},
formatSelection: function() {
var args;
args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
- return _this.formatSelection.apply(_this, args);
+ return self.formatSelection.apply(self, args);
},
- dropdownCssClass: "ajax-groups-dropdown",
+ dropdownCssClass: "ajax-groups-dropdown select2-infinite",
// we do not want to escape markup since we are displaying html in results
escapeMarkup: function(m) {
return m;
}
});
+
+ self.dropdown = document.querySelector('.select2-infinite .select2-results');
+
+ $select.on('select2-loaded', self.forceOverflow.bind(self));
};
})(this));
}
@@ -65,5 +104,12 @@ window.GroupsSelect = (function() {
return group.full_name;
};
+ GroupsSelect.prototype.forceOverflow = function (e) {
+ const itemHeight = this.dropdown.querySelector('.select2-result:first-child').clientHeight;
+ this.dropdown.style.height = `${Math.floor(this.dropdown.scrollHeight - (itemHeight * 0.9))}px`;
+ };
+
+ GroupsSelect.PER_PAGE = 20;
+
return GroupsSelect;
})();
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index a1423b6fda5..4aad0128aef 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -232,6 +232,22 @@
};
/**
+ this will take in the getAllResponseHeaders result and normalize them
+ this way we don't run into production issues when nginx gives us lowercased header keys
+ */
+ w.gl.utils.normalizeCRLFHeaders = (headers) => {
+ const headersObject = {};
+ const headersArray = headers.split('\n');
+
+ headersArray.forEach((header) => {
+ const keyValue = header.split(': ');
+ headersObject[keyValue[0]] = keyValue[1];
+ });
+
+ return w.gl.utils.normalizeHeaders(headersObject);
+ };
+
+ /**
* Parses pagination object string values into numbers.
*
* @param {Object} paginationInformation
diff --git a/spec/features/projects/group_links_spec.rb b/spec/features/projects/group_links_spec.rb
index 8b302a6aa23..83cd28364a3 100644
--- a/spec/features/projects/group_links_spec.rb
+++ b/spec/features/projects/group_links_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Project group links', feature: true, js: true do
+feature 'Project group links', :feature, :js do
include Select2Helper
let(:master) { create(:user) }
@@ -29,4 +29,24 @@ feature 'Project group links', feature: true, js: true do
end
end
end
+
+ describe 'the groups dropdown' do
+ before do
+ group_two = create(:group)
+ group.add_owner(master)
+ group_two.add_owner(master)
+
+ visit namespace_project_settings_members_path(project.namespace, project)
+ execute_script 'GroupsSelect.PER_PAGE = 1;'
+ open_select2 '#link_group_id'
+ end
+
+ it 'should infinitely scroll' do
+ expect(find('.select2-drop .select2-results')).to have_selector('.select2-result', count: 1)
+
+ scroll_select2_to_bottom('.select2-drop .select2-results:visible')
+
+ expect(find('.select2-drop .select2-results')).to have_selector('.select2-result', count: 2)
+ end
+ end
end
diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js
index f4d3e77e515..824f6687292 100644
--- a/spec/javascripts/lib/utils/common_utils_spec.js
+++ b/spec/javascripts/lib/utils/common_utils_spec.js
@@ -108,6 +108,37 @@ require('~/lib/utils/common_utils');
});
});
+ describe('gl.utils.normalizeCRLFHeaders', () => {
+ beforeEach(function () {
+ this.CLRFHeaders = 'a-header: a-value\nAnother-Header: ANOTHER-VALUE\nLaSt-HeAdEr: last-VALUE';
+
+ spyOn(String.prototype, 'split').and.callThrough();
+ spyOn(gl.utils, 'normalizeHeaders').and.callThrough();
+
+ this.normalizeCRLFHeaders = gl.utils.normalizeCRLFHeaders(this.CLRFHeaders);
+ });
+
+ it('should split by newline', function () {
+ expect(String.prototype.split).toHaveBeenCalledWith('\n');
+ });
+
+ it('should split by colon+space for each header', function () {
+ expect(String.prototype.split.calls.allArgs().filter(args => args[0] === ': ').length).toBe(3);
+ });
+
+ it('should call gl.utils.normalizeHeaders with a parsed headers object', function () {
+ expect(gl.utils.normalizeHeaders).toHaveBeenCalledWith(jasmine.any(Object));
+ });
+
+ it('should return a normalized headers object', function () {
+ expect(this.normalizeCRLFHeaders).toEqual({
+ 'A-HEADER': 'a-value',
+ 'ANOTHER-HEADER': 'ANOTHER-VALUE',
+ 'LAST-HEADER': 'last-VALUE',
+ });
+ });
+ });
+
describe('gl.utils.parseIntPagination', () => {
it('should parse to integers all string values and return pagination object', () => {
const pagination = {
diff --git a/spec/support/select2_helper.rb b/spec/support/select2_helper.rb
index 0d526045012..6b1853c2364 100644
--- a/spec/support/select2_helper.rb
+++ b/spec/support/select2_helper.rb
@@ -22,4 +22,12 @@ module Select2Helper
execute_script("$('#{selector}').select2('val', '#{value}').trigger('change');")
end
end
+
+ def open_select2(selector)
+ execute_script("$('#{selector}').select2('open');")
+ end
+
+ def scroll_select2_to_bottom(selector)
+ evaluate_script "$('#{selector}').scrollTop($('#{selector}')[0].scrollHeight); $('#{selector}');"
+ end
end